From 6f8294ad3c86d5e1b3826d2dbf4b8fa7fc69dd4a Mon Sep 17 00:00:00 2001 From: jwaisner Date: Tue, 4 Nov 2025 23:08:00 -0600 Subject: [PATCH 1/7] Added Gradle to build process. --- .gitignore | 5 + build.gradle | 381 ++++++++++++++++++++++++++++++++++++++++++++++ gradle.properties | 19 +++ settings.gradle | 25 +++ 4 files changed, 430 insertions(+) create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore index 207bc8a..f0373c6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,8 @@ # Qodo /.qodo + +# Gradle +.gradle/ +build/ +.gradle-cache/ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..dda9833 --- /dev/null +++ b/build.gradle @@ -0,0 +1,381 @@ +/* + * Bearsampp Module Memcached - Gradle Build + * + * This is a hybrid build configuration that: + * 1. Imports existing Ant build files for backward compatibility + * 2. Provides modern Gradle features (caching, incremental builds, parallel execution) + * 3. Allows gradual migration from Ant to Gradle + * + * Usage: + * gradle tasks - List all available tasks + * gradle release - Interactive release (prompts for version) + * gradle release "-PbundleVersion=1.6.29" - Non-interactive release + * gradle clean - Clean build artifacts + * gradle info - Display build information + */ + +plugins { + id 'base' +} + +// Load build properties +def buildProps = new Properties() +file('build.properties').withInputStream { buildProps.load(it) } + +// Project information +group = 'com.bearsampp.modules' +version = buildProps.getProperty('bundle.release', '1.0.0') +description = "Bearsampp Module - ${buildProps.getProperty('bundle.name', 'memcached')}" + +// Define project paths +ext { + projectBasedir = projectDir.absolutePath + rootDir = projectDir.parent + devPath = file("${rootDir}/dev").absolutePath + buildPropertiesFile = file('build.properties').absolutePath + + // Bundle properties from build.properties + bundleName = buildProps.getProperty('bundle.name', 'memcached') + bundleRelease = buildProps.getProperty('bundle.release', '1.0.0') + bundleType = buildProps.getProperty('bundle.type', 'bins') + bundleFormat = buildProps.getProperty('bundle.format', '7z') +} + +// Verify dev path exists +if (!file(ext.devPath).exists()) { + throw new GradleException("Dev path not found: ${ext.devPath}. Please ensure the 'dev' project exists in ${ext.rootDir}") +} + +// Configure repositories for dependencies +repositories { + mavenCentral() +} + +// ============================================================================ +// ANT INTEGRATION - Import existing Ant build files +// ============================================================================ + +// Set Ant properties before importing +ant.properties['project.basedir'] = ext.projectBasedir +ant.properties['root.dir'] = ext.rootDir +ant.properties['dev.path'] = ext.devPath +ant.properties['build.properties'] = ext.buildPropertiesFile + +// Load build.properties into Ant +ant.property(file: ext.buildPropertiesFile) + +// Import the main Ant build file +// This preserves all existing Ant functionality +ant.importBuild('build.xml') { antTargetName -> + // Map Ant target names to Gradle task names + // Prefix all with 'ant-' to avoid conflicts + return "ant-${antTargetName}".toString() +} + +// ============================================================================ +// GRADLE NATIVE TASKS - Modern alternatives and enhancements +// ============================================================================ + +// Task: Display build information +tasks.register('info') { + group = 'help' + description = 'Display build configuration information' + + def projectName = project.name + def projectVersion = project.version + def projectDescription = project.description + def gradleVersion = gradle.gradleVersion + def gradleHome = gradle.gradleHomeDir + + doLast { + println """ + ================================================================ + Bearsampp Module Memcached - Build Info + ================================================================ + + Project: ${projectName} + Version: ${projectVersion} + Description: ${projectDescription} + + Bundle Properties: + Name: ${bundleName} + Release: ${bundleRelease} + Type: ${bundleType} + Format: ${bundleFormat} + + Paths: + Project Dir: ${projectBasedir} + Root Dir: ${rootDir} + Dev Path: ${devPath} + + Java: + Version: ${JavaVersion.current()} + Home: ${System.getProperty('java.home')} + + Gradle: + Version: ${gradleVersion} + Home: ${gradleHome} + + Available Task Groups: + * build - Build and package tasks + * ant tasks - Legacy Ant tasks (prefixed with 'ant-') + * help - Help and information tasks + + Quick Start: + gradle tasks - List all available tasks + gradle info - Show this information + gradle release - Interactive release build + gradle release "-PbundleVersion=1.6.29" - Non-interactive release + gradle clean - Clean build artifacts + gradle verify - Verify build environment + """.stripIndent() + } +} + +// Task: Main release task - supports both interactive and non-interactive modes +tasks.register('release') { + group = 'build' + description = 'Build release package (interactive or use -PbundleVersion=X.X.X for non-interactive)' + + // Ensure libraries are loaded first + dependsOn 'ant-load.lib' + + doLast { + def versionToBuild = project.findProperty('bundleVersion') + + if (versionToBuild) { + // Non-interactive mode with specified version + println "=".multiply(70) + println "Building release for ${bundleName} version ${versionToBuild}..." + println "=".multiply(70) + + def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}") + + if (!bundlePath.exists()) { + def availableVersions = file("${projectDir}/bin").listFiles() + .findAll { it.isDirectory() && it.name.startsWith(bundleName) } + .collect { " - " + it.name.replace(bundleName, '') } + .join('\n') + + throw new GradleException("Bundle version not found: ${bundlePath}\n\nAvailable versions in bin/:\n${availableVersions}") + } + + println "Bundle path: ${bundlePath}" + println "" + + // Execute Ant command directly to avoid Gradle Ant integration issues + def antCommand = ["cmd", "/c", "ant", "release", "-Dinput.bundle=${versionToBuild}"] + println "Executing: ant release -Dinput.bundle=${versionToBuild}" + println "" + + def process = antCommand.execute(null, projectDir) + process.consumeProcessOutput(System.out, System.err) + def exitCode = process.waitFor() + + if (exitCode != 0) { + throw new GradleException("Ant release failed with exit code: ${exitCode}") + } + + println "" + println "=".multiply(70) + println "[SUCCESS] Release build completed successfully for version ${versionToBuild}" + println "=".multiply(70) + } else { + // Interactive mode - call Ant release target which will prompt for input + println "=".multiply(70) + println "Starting interactive release build..." + println "You will be prompted to enter the bundle version." + println "=".multiply(70) + println "" + + // Call the imported ant-release target for interactive mode + tasks.getByName('ant-release').actions.each { action -> + action.execute(tasks.getByName('ant-release')) + } + + println "" + println "=".multiply(70) + println "[SUCCESS] Release build completed" + println "=".multiply(70) + } + } +} + +// Task: Enhanced clean task +tasks.named('clean') { + group = 'build' + description = 'Clean build artifacts and temporary files' + + doLast { + // Clean Gradle build directory + def buildDir = file("${projectDir}/build") + if (buildDir.exists()) { + delete buildDir + } + + // Clean any temporary directories that might be created + // Use manual directory traversal to avoid fileTree default excludes issue + def tmpDirs = [] + projectDir.eachFileRecurse { file -> + if (file.isDirectory() && (file.name == 'tmp' || file.name == '.tmp')) { + tmpDirs.add(file) + } + } + tmpDirs.each { dir -> + if (dir.exists()) { + delete dir + } + } + + println "[SUCCESS] Build artifacts cleaned" + } +} + +// Task: Verify build environment +tasks.register('verify') { + group = 'verification' + description = 'Verify build environment and dependencies' + + doLast { + println "Verifying build environment for module-memcached..." + + def checks = [:] + + // Check Java version + def javaVersion = JavaVersion.current() + checks['Java 8+'] = javaVersion >= JavaVersion.VERSION_1_8 + + // Check required files + checks['build.xml'] = file('build.xml').exists() + checks['build.properties'] = file('build.properties').exists() + checks['releases.properties'] = file('releases.properties').exists() + + // Check dev directory and required build files + checks['dev directory'] = file(devPath).exists() + checks['build-commons.xml'] = file("${devPath}/build/build-commons.xml").exists() + checks['build-bundle.xml'] = file("${devPath}/build/build-bundle.xml").exists() + + println "\nEnvironment Check Results:" + println "-".multiply(60) + checks.each { name, passed -> + def status = passed ? "[PASS]" : "[FAIL]" + println " ${status.padRight(10)} ${name}" + } + println "-".multiply(60) + + def allPassed = checks.values().every { it } + if (allPassed) { + println "\n[SUCCESS] All checks passed! Build environment is ready." + println "\nYou can now run:" + println " gradle release - Interactive release" + println " gradle release \"-PbundleVersion=1.6.29\" - Non-interactive release" + } else { + println "\n[WARNING] Some checks failed. Please review the requirements." + throw new GradleException("Build environment verification failed") + } + } +} + +// Task: List all bundle versions from releases.properties +tasks.register('listReleases') { + group = 'help' + description = 'List all available releases from releases.properties' + + doLast { + def releasesFile = file('releases.properties') + if (!releasesFile.exists()) { + println "releases.properties not found" + return + } + + def releases = new Properties() + releasesFile.withInputStream { releases.load(it) } + + println "\nAvailable Memcached Releases:" + println "-".multiply(80) + releases.sort { it.key }.each { version, url -> + println " ${version.padRight(10)} -> ${url}" + } + println "-".multiply(80) + println "Total releases: ${releases.size()}" + } +} + +// Task: List available bundle versions in bin directory +tasks.register('listVersions') { + group = 'help' + description = 'List all available bundle versions in bin/ directory' + + doLast { + def binDir = file("${projectDir}/bin") + if (!binDir.exists()) { + println "bin/ directory not found" + return + } + + def versions = binDir.listFiles() + .findAll { it.isDirectory() && it.name.startsWith(bundleName) } + .collect { it.name.replace(bundleName, '') } + .sort() + + println "\nAvailable ${bundleName} versions in bin/:" + println "-".multiply(60) + versions.each { version -> + println " ${version}" + } + println "-".multiply(60) + println "Total versions: ${versions.size()}" + println "\nTo build a specific version:" + println " gradle release \"-PbundleVersion=${versions.last()}\"" + } +} + +// Task: Validate build.properties +tasks.register('validateProperties') { + group = 'verification' + description = 'Validate build.properties configuration' + + doLast { + println "Validating build.properties..." + + def required = ['bundle.name', 'bundle.release', 'bundle.type', 'bundle.format'] + def missing = [] + + required.each { prop -> + if (!buildProps.containsKey(prop) || buildProps.getProperty(prop).trim().isEmpty()) { + missing.add(prop) + } + } + + if (missing.isEmpty()) { + println "[SUCCESS] All required properties are present:" + required.each { prop -> + println " ${prop} = ${buildProps.getProperty(prop)}" + } + } else { + println "[ERROR] Missing required properties:" + missing.each { prop -> + println " - ${prop}" + } + throw new GradleException("build.properties validation failed") + } + } +} + +// ============================================================================ +// BUILD LIFECYCLE HOOKS +// ============================================================================ + +gradle.taskGraph.whenReady { graph -> + println """ + ================================================================ + Bearsampp Module Memcached - Gradle + Ant Hybrid Build + ================================================================ + """.stripIndent() +} + +// ============================================================================ +// DEFAULT TASK +// ============================================================================ + +defaultTasks 'info' diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..19d23f2 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,19 @@ +# Gradle Build Properties for Bearsampp Module Memcached + +# Gradle daemon configuration +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true + +# JVM settings for Gradle +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError + +# Configure console output +org.gradle.console=auto +org.gradle.warning.mode=all + +# Build performance +org.gradle.configureondemand=false + +# Gradle version compatibility +# This project is compatible with Gradle 7.0+ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..37cce3a --- /dev/null +++ b/settings.gradle @@ -0,0 +1,25 @@ +/* + * Bearsampp Module Memcached - Gradle Settings + */ + +rootProject.name = 'module-memcached' + +// Enable Gradle features for better performance +enableFeaturePreview('STABLE_CONFIGURATION_CACHE') + +// Configure build cache for faster builds +buildCache { + local { + enabled = true + directory = file("${rootDir}/.gradle/build-cache") + } +} + +// Display initialization message +gradle.rootProject { + println """ + ================================================================ + Initializing Bearsampp Module Memcached Build + ================================================================ + """.stripIndent() +} From f9abd593b43f2ebe11e987a896e11b67bef2f496 Mon Sep 17 00:00:00 2001 From: Bear Date: Fri, 14 Nov 2025 01:55:31 -0600 Subject: [PATCH 2/7] Update README with comprehensive Gradle build documentation and reorganize archived versions --- .gitignore | 5 + .gradle-docs/CONFIGURATION.md | 746 +++++++++++ .gradle-docs/INDEX.md | 313 +++++ .gradle-docs/MIGRATION-GUIDE.md | 603 +++++++++ .gradle-docs/README.md | 413 ++++++ .gradle-docs/RELEASE-PROCESS.md | 799 +++++++++++ .gradle-docs/TASKS.md | 594 +++++++++ BUILD-SYSTEM.md | 418 ++++++ DESIGN-DECISIONS.md | 371 ++++++ FINAL-SUMMARY.md | 295 ++++ MIGRATION-SUMMARY.md | 298 +++++ README.md | 181 ++- .../memcached1.6.15/bearsampp.conf | 0 .../memcached1.6.17/bearsampp.conf | 0 .../memcached1.6.18/bearsampp.conf | 0 .../memcached1.6.21/bearsampp.conf | 0 .../memcached1.6.24/bearsampp.conf | 0 .../memcached1.6.29/bearsampp.conf | 0 .../memcached1.6.31/bearsampp.conf | 0 .../memcached1.6.32/bearsampp.conf | 0 .../memcached1.6.33/bearsampp.conf | 0 .../memcached1.6.36/bearsampp.conf | 0 .../memcached1.6.37/bearsampp.conf | 0 .../memcached1.6.37/memcached.exe | Bin .../memcached1.6.38/bearsampp.conf | 0 build.gradle | 802 ++++++++--- build.gradle.bruno-reference | 1183 +++++++++++++++++ build.xml | 38 - gradle.properties | 31 +- settings.gradle | 15 - 30 files changed, 6844 insertions(+), 261 deletions(-) create mode 100644 .gradle-docs/CONFIGURATION.md create mode 100644 .gradle-docs/INDEX.md create mode 100644 .gradle-docs/MIGRATION-GUIDE.md create mode 100644 .gradle-docs/README.md create mode 100644 .gradle-docs/RELEASE-PROCESS.md create mode 100644 .gradle-docs/TASKS.md create mode 100644 BUILD-SYSTEM.md create mode 100644 DESIGN-DECISIONS.md create mode 100644 FINAL-SUMMARY.md create mode 100644 MIGRATION-SUMMARY.md rename bin/{ => archived}/memcached1.6.15/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.17/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.18/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.21/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.24/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.29/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.31/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.32/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.33/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.36/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.37/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.37/memcached.exe (100%) rename bin/{ => archived}/memcached1.6.38/bearsampp.conf (100%) create mode 100644 build.gradle.bruno-reference delete mode 100644 build.xml diff --git a/.gitignore b/.gitignore index f0373c6..6fa02e0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ .gradle/ build/ .gradle-cache/ +.gradle/build-cache/ + +# Ant (legacy - no longer used) +build.xml +*.ant diff --git a/.gradle-docs/CONFIGURATION.md b/.gradle-docs/CONFIGURATION.md new file mode 100644 index 0000000..26df214 --- /dev/null +++ b/.gradle-docs/CONFIGURATION.md @@ -0,0 +1,746 @@ +# Configuration Guide + +Complete guide for configuring the Bearsampp Module Memcached build system. + +## Table of Contents + +- [Configuration Files](#configuration-files) +- [Build Properties](#build-properties) +- [Gradle Properties](#gradle-properties) +- [Release Properties](#release-properties) +- [Environment Variables](#environment-variables) +- [Advanced Configuration](#advanced-configuration) + +## Configuration Files + +### Overview + +The build system uses three main configuration files: + +| File | Purpose | Format | Required | +|------------------------|------------------------------------------|--------------|----------| +| `build.properties` | Bundle configuration | Properties | Yes | +| `gradle.properties` | Gradle build settings | Properties | Yes | +| `releases.properties` | Release history and URLs | Properties | Yes | +| `settings.gradle` | Gradle project settings | Groovy | Yes | + +### File Locations + +``` +module-memcached/ +├── build.properties # Bundle configuration +├── gradle.properties # Gradle settings +├── releases.properties # Release history +└── settings.gradle # Project settings +``` + +--- + +## Build Properties + +### File: build.properties + +Main configuration file for bundle settings. + +**Location:** `E:/Bearsampp-development/module-memcached/build.properties` + +**Format:** Java Properties + +### Properties Reference + +#### bundle.name + +Bundle name identifier. + +| Property | Type | Required | Default | Example | +|------------------|----------|----------|--------------|--------------| +| `bundle.name` | String | Yes | `memcached` | `memcached` | + +**Description:** Identifies the bundle name used in file naming and directory structure. + +**Usage:** +```properties +bundle.name = memcached +``` + +**Impact:** +- Used in archive naming: `bearsampp-${bundle.name}-${version}-${release}.7z` +- Used in directory naming: `bin/${bundle.name}${version}/` +- Used in display output + +--- + +#### bundle.release + +Release date identifier. + +| Property | Type | Required | Default | Example | +|------------------|----------|----------|--------------|--------------| +| `bundle.release` | String | Yes | - | `2025.8.20` | + +**Description:** Release date in YYYY.M.D format. + +**Usage:** +```properties +bundle.release = 2025.8.20 +``` + +**Format:** `YYYY.M.D` or `YYYY.MM.DD` + +**Examples:** +- `2025.8.20` - August 20, 2025 +- `2025.12.1` - December 1, 2025 +- `2024.3.30` - March 30, 2024 + +**Impact:** +- Used in archive naming +- Used in release URLs +- Used in version tracking + +--- + +#### bundle.type + +Bundle type classification. + +| Property | Type | Required | Default | Example | +|------------------|----------|----------|--------------|--------------| +| `bundle.type` | String | Yes | `bins` | `bins` | + +**Description:** Classifies the bundle type. + +**Usage:** +```properties +bundle.type = bins +``` + +**Valid Values:** +- `bins` - Binary distribution +- `apps` - Application distribution +- `tools` - Tool distribution + +**Impact:** +- Used for categorization +- May affect build process in future versions + +--- + +#### bundle.format + +Archive format for release packages. + +| Property | Type | Required | Default | Example | +|------------------|----------|----------|--------------|--------------| +| `bundle.format` | String | Yes | `7z` | `7z` | + +**Description:** Specifies the archive format for release packages. + +**Usage:** +```properties +bundle.format = 7z +``` + +**Valid Values:** +- `7z` - 7-Zip format (recommended) +- `zip` - ZIP format + +**Impact:** +- Determines compression tool used +- Affects archive file extension +- Impacts compression ratio + +**Requirements:** +- `7z` format requires 7-Zip installed and in PATH +- `zip` format uses Gradle's built-in zip task + +--- + +#### build.path (Optional) + +Custom build output directory. + +| Property | Type | Required | Default | Example | +|------------------|----------|----------|--------------------------------------|----------------------------| +| `build.path` | String | No | `${user.home}/Bearsampp-build` | `C:/Bearsampp-build` | + +**Description:** Specifies custom build output directory. + +**Usage:** +```properties +build.path = C:/Bearsampp-build +``` + +**Default Behavior:** +If not specified, uses `${user.home}/Bearsampp-build` + +**Directory Structure:** +``` +${build.path}/ +├── tmp/ # Temporary build files +│ └── memcached1.6.39/ # Prepared bundle +└── release/ # Release archives + └── bearsampp-memcached-1.6.39-2025.8.20.7z +``` + +--- + +### Complete Example + +```properties +# Bundle Configuration +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z + +# Optional: Custom build path +#build.path = C:/Bearsampp-build +``` + +--- + +## Gradle Properties + +### File: gradle.properties + +Gradle build system configuration. + +**Location:** `E:/Bearsampp-development/module-memcached/gradle.properties` + +**Format:** Java Properties + +### Properties Reference + +#### org.gradle.daemon + +Enable Gradle daemon for faster builds. + +| Property | Type | Required | Default | Example | +|--------------------------|-----------|----------|--------------|--------------| +| `org.gradle.daemon` | Boolean | No | `true` | `true` | + +**Description:** Enables Gradle daemon for improved build performance. + +**Usage:** +```properties +org.gradle.daemon=true +``` + +**Benefits:** +- Faster build times +- Reduced startup overhead +- Improved incremental builds + +--- + +#### org.gradle.parallel + +Enable parallel task execution. + +| Property | Type | Required | Default | Example | +|--------------------------|-----------|----------|--------------|--------------| +| `org.gradle.parallel` | Boolean | No | `true` | `true` | + +**Description:** Enables parallel execution of independent tasks. + +**Usage:** +```properties +org.gradle.parallel=true +``` + +**Benefits:** +- Faster builds on multi-core systems +- Better resource utilization + +--- + +#### org.gradle.caching + +Enable build caching. + +| Property | Type | Required | Default | Example | +|--------------------------|-----------|----------|--------------|--------------| +| `org.gradle.caching` | Boolean | No | `true` | `true` | + +**Description:** Enables Gradle build cache for faster incremental builds. + +**Usage:** +```properties +org.gradle.caching=true +``` + +**Benefits:** +- Reuses outputs from previous builds +- Significantly faster incremental builds +- Shared cache across projects + +--- + +#### org.gradle.configureondemand + +Enable configuration on demand. + +| Property | Type | Required | Default | Example | +|----------------------------------|-----------|----------|--------------|--------------| +| `org.gradle.configureondemand` | Boolean | No | `true` | `true` | + +**Description:** Configures only necessary projects. + +**Usage:** +```properties +org.gradle.configureondemand=true +``` + +**Benefits:** +- Faster configuration phase +- Reduced memory usage + +--- + +#### org.gradle.jvmargs + +JVM arguments for Gradle. + +| Property | Type | Required | Default | Example | +|--------------------------|-----------|----------|--------------|----------------------------| +| `org.gradle.jvmargs` | String | No | - | `-Xmx2048m -Xms512m` | + +**Description:** Specifies JVM arguments for Gradle daemon. + +**Usage:** +```properties +org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m +``` + +**Common Arguments:** +- `-Xmx2048m` - Maximum heap size (2GB) +- `-Xms512m` - Initial heap size (512MB) +- `-XX:MaxMetaspaceSize=512m` - Maximum metaspace size + +--- + +### Complete Example + +```properties +# Gradle Build Configuration + +# Enable daemon for faster builds +org.gradle.daemon=true + +# Enable parallel execution +org.gradle.parallel=true + +# Enable build caching +org.gradle.caching=true + +# Enable configuration on demand +org.gradle.configureondemand=true + +# JVM arguments +org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m + +# Console output +org.gradle.console=rich + +# Warning mode +org.gradle.warning.mode=all +``` + +--- + +## Release Properties + +### File: releases.properties + +Historical release information and download URLs. + +**Location:** `E:/Bearsampp-development/module-memcached/releases.properties` + +**Format:** Java Properties + +### Format + +```properties + = +``` + +### Properties Reference + +Each entry represents a released version: + +| Key | Value | Description | +|-------------|------------------------------------------|---------------------------------------| +| Version | Download URL | GitHub release download link | + +### Example Entries + +```properties +1.6.39 = https://github.com/Bearsampp/module-memcached/releases/download/2025.8.20/bearsampp-memcached-1.6.39-2025.8.20.7z +1.6.38 = https://github.com/Bearsampp/module-memcached/releases/download/2025.4.19/bearsampp-memcached-1.6.38-2025.4.19.7z +1.6.36 = https://github.com/Bearsampp/module-memcached/releases/download/2025.2.11/bearsampp-memcached-1.6.36-2025.2.11.7z +``` + +### URL Format + +``` +https://github.com/Bearsampp/module-memcached/releases/download/{release_date}/bearsampp-memcached-{version}-{release_date}.7z +``` + +**Components:** +- `{release_date}` - Release date from `bundle.release` +- `{version}` - Memcached version number +- `.7z` - Archive extension + +### Adding New Releases + +When creating a new release: + +1. Build the release package +2. Upload to GitHub releases +3. Add entry to `releases.properties`: + +```properties +1.6.40 = https://github.com/Bearsampp/module-memcached/releases/download/2025.9.15/bearsampp-memcached-1.6.40-2025.9.15.7z +``` + +### Sorting + +Entries should be sorted by version number (ascending): + +```properties +1.6.15 = ... +1.6.17 = ... +1.6.18 = ... +1.6.39 = ... +``` + +--- + +## Environment Variables + +### Required Variables + +#### JAVA_HOME + +Java installation directory. + +| Variable | Required | Description | Example | +|--------------|----------|---------------------------------------|----------------------------------------| +| `JAVA_HOME` | Yes | Java JDK installation directory | `C:\Program Files\Java\jdk-17.0.2` | + +**Usage:** +```bash +# Windows +set JAVA_HOME=C:\Program Files\Java\jdk-17.0.2 + +# PowerShell +$env:JAVA_HOME="C:\Program Files\Java\jdk-17.0.2" +``` + +**Verification:** +```bash +echo %JAVA_HOME% +java -version +``` + +--- + +#### PATH + +System path including required tools. + +| Variable | Required | Description | Example | +|--------------|----------|---------------------------------------|----------------------------------------| +| `PATH` | Yes | System path with 7-Zip | `C:\Program Files\7-Zip;...` | + +**Required in PATH:** +- 7-Zip (for 7z format) +- Java (for Gradle) +- Gradle (optional, can use wrapper) + +**Usage:** +```bash +# Windows +set PATH=%PATH%;C:\Program Files\7-Zip + +# PowerShell +$env:PATH="$env:PATH;C:\Program Files\7-Zip" +``` + +**Verification:** +```bash +7z +gradle --version +``` + +--- + +### Optional Variables + +#### GRADLE_HOME + +Gradle installation directory. + +| Variable | Required | Description | Example | +|----------------|----------|---------------------------------------|----------------------------------------| +| `GRADLE_HOME` | No | Gradle installation directory | `C:\Gradle\gradle-8.5` | + +**Usage:** +```bash +# Windows +set GRADLE_HOME=C:\Gradle\gradle-8.5 +set PATH=%PATH%;%GRADLE_HOME%\bin +``` + +--- + +#### GRADLE_USER_HOME + +Gradle user home directory. + +| Variable | Required | Description | Example | +|----------------------|----------|---------------------------------------|----------------------------------------| +| `GRADLE_USER_HOME` | No | Gradle cache and config directory | `C:\Users\troy\.gradle` | + +**Default:** `${user.home}/.gradle` + +**Usage:** +```bash +# Windows +set GRADLE_USER_HOME=C:\Users\troy\.gradle +``` + +--- + +## Advanced Configuration + +### Custom Build Paths + +Override default build paths: + +```properties +# build.properties +build.path = D:/CustomBuild +``` + +**Directory Structure:** +``` +D:/CustomBuild/ +├── tmp/ +│ └── memcached1.6.39/ +└── release/ + └── bearsampp-memcached-1.6.39-2025.8.20.7z +``` + +--- + +### Performance Tuning + +Optimize Gradle performance: + +```properties +# gradle.properties + +# Increase heap size for large builds +org.gradle.jvmargs=-Xmx4096m -Xms1024m + +# Enable all performance features +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true + +# File system watching (Gradle 7.0+) +org.gradle.vfs.watch=true +``` + +--- + +### Network Configuration + +Configure network settings: + +```properties +# gradle.properties + +# Proxy settings +systemProp.http.proxyHost=proxy.company.com +systemProp.http.proxyPort=8080 +systemProp.https.proxyHost=proxy.company.com +systemProp.https.proxyPort=8080 + +# Proxy authentication +systemProp.http.proxyUser=username +systemProp.http.proxyPassword=password +``` + +--- + +### Logging Configuration + +Configure logging levels: + +```properties +# gradle.properties + +# Console output +org.gradle.console=rich + +# Logging level +org.gradle.logging.level=lifecycle + +# Warning mode +org.gradle.warning.mode=all +``` + +**Console Options:** +- `auto` - Automatic detection +- `plain` - Plain text output +- `rich` - Rich console output (default) +- `verbose` - Verbose output + +**Warning Modes:** +- `all` - Show all warnings +- `fail` - Fail on warnings +- `summary` - Show warning summary +- `none` - Suppress warnings + +--- + +### Build Cache Configuration + +Configure build cache: + +```groovy +// settings.gradle + +buildCache { + local { + enabled = true + directory = file("${rootDir}/.gradle/build-cache") + removeUnusedEntriesAfterDays = 30 + } + + remote(HttpBuildCache) { + enabled = false + url = 'https://cache.example.com/' + push = false + } +} +``` + +--- + +### Multi-Project Configuration + +For multi-project builds: + +```groovy +// settings.gradle + +rootProject.name = 'module-memcached' + +// Include subprojects +// include 'subproject1' +// include 'subproject2' + +// Enable features +enableFeaturePreview('STABLE_CONFIGURATION_CACHE') +enableFeaturePreview('TYPESAFE_PROJECT_ACCESSORS') +``` + +--- + +## Configuration Validation + +### Validate Configuration + +```bash +# Validate build.properties +gradle validateProperties + +# Verify environment +gradle verify + +# Display configuration +gradle info +``` + +### Configuration Checklist + +- [ ] `build.properties` exists and is valid +- [ ] `gradle.properties` exists and is configured +- [ ] `releases.properties` exists and is up-to-date +- [ ] `settings.gradle` exists +- [ ] `JAVA_HOME` is set correctly +- [ ] 7-Zip is in PATH +- [ ] Gradle is installed or wrapper is present +- [ ] `bin/` directory contains version folders + +--- + +## Troubleshooting Configuration + +### Common Issues + +#### Issue: Property not found + +**Error:** +``` +Property 'bundle.name' not found +``` + +**Solution:** +Check `build.properties` contains all required properties: +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +--- + +#### Issue: Invalid property value + +**Error:** +``` +Invalid value for bundle.format: 'rar' +``` + +**Solution:** +Use valid values: +```properties +bundle.format = 7z # or 'zip' +``` + +--- + +#### Issue: Path not found + +**Error:** +``` +Build path not found: C:/Invalid/Path +``` + +**Solution:** +1. Check path exists +2. Use forward slashes or escaped backslashes +3. Remove `build.path` to use default + +```properties +# Correct +build.path = C:/Bearsampp-build + +# Also correct +build.path = C:\\Bearsampp-build + +# Use default +#build.path = C:/Bearsampp-build +``` + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/INDEX.md b/.gradle-docs/INDEX.md new file mode 100644 index 0000000..ca1cd42 --- /dev/null +++ b/.gradle-docs/INDEX.md @@ -0,0 +1,313 @@ +# Bearsampp Module Memcached - Documentation Index + +Complete documentation for the Bearsampp Module Memcached build system. + +## Documentation Structure + +``` +.gradle-docs/ +├── INDEX.md # This file - Documentation index +├── README.md # Main documentation and quick start +├── TASKS.md # Complete task reference +├── CONFIGURATION.md # Configuration guide +├── RELEASE-PROCESS.md # Release process guide +└── MIGRATION-GUIDE.md # Migration guide from Ant to Gradle +``` + +## Quick Navigation + +### Getting Started + +| Document | Description | Audience | +|--------------------------------------------------|------------------------------------------------|-------------------------| +| [README.md](README.md) | Main documentation and quick start | All users | + +### Reference Documentation + +| Document | Description | Audience | +|--------------------------------------------------|------------------------------------------------|-------------------------| +| [TASKS.md](TASKS.md) | Complete task reference | Developers | +| [CONFIGURATION.md](CONFIGURATION.md) | Configuration guide | Developers, DevOps | +| [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Release process guide | Maintainers | +| [MIGRATION-GUIDE.md](MIGRATION-GUIDE.md) | Migration guide from Ant to Gradle | All users | + +## Documentation by Topic + +### Build System + +| Topic | Document | Section | +|-------------------------------|--------------------------------------------------|-----------------------------------| +| Overview | [README.md](README.md) | Build System | +| Architecture | [README.md](README.md) | Build System > Architecture | +| Build Flow | [README.md](README.md) | Build System > Build Flow | + +### Tasks + +| Topic | Document | Section | +|-------------------------------|--------------------------------------------------|-----------------------------------| +| All Tasks | [TASKS.md](TASKS.md) | - | +| Build Tasks | [TASKS.md](TASKS.md) | Build Tasks | +| Verification Tasks | [TASKS.md](TASKS.md) | Verification Tasks | +| Help Tasks | [TASKS.md](TASKS.md) | Help Tasks | +| Documentation Tasks | [TASKS.md](TASKS.md) | Documentation Tasks | +| Task Dependencies | [TASKS.md](TASKS.md) | Task Dependencies | + +### Configuration + +| Topic | Document | Section | +|-------------------------------|--------------------------------------------------|-----------------------------------| +| Configuration Files | [CONFIGURATION.md](CONFIGURATION.md) | Configuration Files | +| Build Properties | [CONFIGURATION.md](CONFIGURATION.md) | Build Properties | +| Gradle Properties | [CONFIGURATION.md](CONFIGURATION.md) | Gradle Properties | +| Release Properties | [CONFIGURATION.md](CONFIGURATION.md) | Release Properties | +| Environment Variables | [CONFIGURATION.md](CONFIGURATION.md) | Environment Variables | +| Advanced Configuration | [CONFIGURATION.md](CONFIGURATION.md) | Advanced Configuration | + +### Release Process + +| Topic | Document | Section | +|-------------------------------|--------------------------------------------------|-----------------------------------| +| Overview | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Overview | +| Prerequisites | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Prerequisites | +| Release Workflow | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Release Workflow | +| Step-by-Step Guide | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Step-by-Step Guide | +| Version Management | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Version Management | +| Publishing Releases | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Publishing Releases | +| Post-Release Tasks | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Post-Release Tasks | + +### Troubleshooting + +| Topic | Document | Section | +|-------------------------------|--------------------------------------------------|-----------------------------------| +| Common Issues | [README.md](README.md) | Troubleshooting | +| Configuration Issues | [CONFIGURATION.md](CONFIGURATION.md) | Troubleshooting Configuration | +| Release Issues | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Troubleshooting | + +## Common Tasks + +### For New Users + +1. Read [README.md](README.md) - Overview and quick start +2. Run `gradle info` - Display build information +3. Run `gradle verify` - Verify build environment +4. Run `gradle listVersions` - List available versions + +### For Developers + +1. Read [TASKS.md](TASKS.md) - Learn available tasks +2. Read [CONFIGURATION.md](CONFIGURATION.md) - Understand configuration +3. Run `gradle tasks` - List all tasks +4. Run `gradle release -PbundleVersion=1.6.39` - Build a release + +### For Maintainers + +1. Read [RELEASE-PROCESS.md](RELEASE-PROCESS.md) - Learn release process +2. Follow step-by-step guide for releases +3. Update documentation as needed +4. Monitor issues and feedback + +## Quick Reference + +### Essential Commands + +```bash +# Display build information +gradle info + +# List available versions +gradle listVersions + +# List all releases +gradle listReleases + +# Verify environment +gradle verify + +# Build release +gradle release -PbundleVersion=1.6.39 + +# Clean build artifacts +gradle clean +``` + +### Essential Files + +| File | Purpose | Documentation | +|------------------------|------------------------------------------|-----------------------------------------------| +| `build.gradle` | Main build script | [README.md](README.md) | +| `build.properties` | Bundle configuration | [CONFIGURATION.md](CONFIGURATION.md) | +| `gradle.properties` | Gradle settings | [CONFIGURATION.md](CONFIGURATION.md) | +| `releases.properties` | Release history | [CONFIGURATION.md](CONFIGURATION.md) | +| `settings.gradle` | Project settings | [README.md](README.md) | + +### Essential Directories + +| Directory | Purpose | Documentation | +|-----------------------|------------------------------------------|-----------------------------------------------| +| `bin/` | Memcached version binaries | [README.md](README.md) | +| `.gradle-docs/` | Build documentation | This file | +| `build/` | Gradle build output | [README.md](README.md) | + +## Documentation Standards + +### Format + +All documentation uses Markdown format with: + +- Clear headings and structure +- Tables for reference information +- Code blocks for examples +- Consistent formatting + +### Tables + +Tables are aligned with consistent column widths: + +```markdown +| Column 1 | Column 2 | Column 3 | +|---------------------|---------------------|---------------------| +| Value 1 | Value 2 | Value 3 | +``` + +### Code Blocks + +Code blocks specify the language: + +```markdown +```bash +gradle info +``` +``` + +### Links + +Internal links use relative paths: + +```markdown +[README.md](README.md) +``` + +External links use full URLs: + +```markdown +[Bearsampp](https://bearsampp.com) +``` + +## Maintenance + +### Updating Documentation + +When updating documentation: + +1. Update relevant document(s) +2. Update this index if structure changes +3. Update version and date at bottom +4. Test all links +5. Verify formatting + +### Version Information + +All documentation includes version information: + +```markdown +--- + +**Last Updated:** YYYY-MM-DD +**Version:** YYYY.M.D +**Maintainer:** Bearsampp Team +``` + +### Review Schedule + +Documentation should be reviewed: + +- After each release +- When build system changes +- When new features are added +- Quarterly for accuracy + +## Contributing to Documentation + +### Guidelines + +- Use clear, concise language +- Provide examples +- Keep tables aligned +- Test all commands +- Update index when adding sections + +### Style Guide + +- Use present tense +- Use active voice +- Use consistent terminology +- Use proper Markdown formatting +- Include code examples + +### Submitting Changes + +1. Fork the repository +2. Create a documentation branch +3. Make your changes +4. Test all examples +5. Submit a pull request + +## Support + +### Getting Help + +- Read the documentation +- Check troubleshooting sections +- Search GitHub issues +- Ask in discussions + +### Reporting Issues + +Report documentation issues at: + +https://github.com/bearsampp/bearsampp/issues + +Include: + +- Document name +- Section name +- Issue description +- Suggested improvement + +## External Resources + +### Gradle + +- [Gradle Documentation](https://docs.gradle.org/) +- [Gradle User Manual](https://docs.gradle.org/current/userguide/userguide.html) +- [Gradle Build Language Reference](https://docs.gradle.org/current/dsl/) + +### Memcached + +- [Memcached Official](https://memcached.org/) +- [Memcached Documentation](https://github.com/memcached/memcached/wiki) +- [Memcached Downloads](https://memcached.org/downloads) + +### Bearsampp + +- [Bearsampp Project](https://github.com/bearsampp/bearsampp) +- [Bearsampp Website](https://bearsampp.com) +- [Bearsampp Documentation](https://bearsampp.com/docs) + +### Tools + +- [7-Zip](https://www.7-zip.org/) +- [Git](https://git-scm.com/) +- [Java](https://adoptium.net/) + +## Document History + +| Version | Date | Changes | +|-------------|--------------|--------------------------------------------------| +| 2025.8.20 | 2025-08-20 | Initial documentation for pure Gradle build | + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/MIGRATION-GUIDE.md b/.gradle-docs/MIGRATION-GUIDE.md new file mode 100644 index 0000000..1b105d2 --- /dev/null +++ b/.gradle-docs/MIGRATION-GUIDE.md @@ -0,0 +1,603 @@ +# Migration Guide: Ant to Pure Gradle + +Guide for migrating from the Ant-based build system to the pure Gradle build system. + +## Table of Contents + +- [Overview](#overview) +- [What Changed](#what-changed) +- [Command Mapping](#command-mapping) +- [Configuration Changes](#configuration-changes) +- [Benefits](#benefits) +- [Migration Steps](#migration-steps) +- [Troubleshooting](#troubleshooting) + +## Overview + +The Bearsampp Module Memcached build system has been migrated from a hybrid Ant/Gradle system to a pure Gradle build system. + +### Migration Summary + +| Aspect | Before (Ant/Gradle) | After (Pure Gradle) | +|----------------------|----------------------------------------|----------------------------------------| +| Build System | Hybrid Ant + Gradle | Pure Gradle | +| Build Files | `build.xml`, `build.gradle` | `build.gradle` only | +| Task Execution | Ant tasks via Gradle wrapper | Native Gradle tasks | +| Dependencies | External Ant build files | Self-contained | +| Documentation | Scattered | Centralized in `.gradle-docs/` | + +## What Changed + +### Removed + +| Item | Reason | +|------------------------------|--------------------------------------------------| +| `build.xml` | Replaced by native Gradle tasks | +| Ant task imports | No longer needed | +| External Ant dependencies | Build is now self-contained | +| Ant-specific properties | Converted to Gradle properties | + +### Added + +| Item | Purpose | +|------------------------------|--------------------------------------------------| +| Pure Gradle tasks | Native Gradle implementation | +| `.gradle-docs/` | Comprehensive documentation | +| Enhanced output formatting | Better user experience | +| Build verification | Environment checking | + +### Modified + +| Item | Change | +|------------------------------|--------------------------------------------------| +| `build.gradle` | Removed Ant imports, added native tasks | +| `settings.gradle` | Removed Ant references | +| `gradle.properties` | Added performance optimizations | +| `README.md` | Updated with new documentation structure | + +## Command Mapping + +### Build Commands + +| Old Command (Ant) | New Command (Gradle) | +|-------------------------------------------|---------------------------------------------------| +| `ant release -Dinput.bundle=1.6.39` | `gradle release -PbundleVersion=1.6.39` | +| `ant clean` | `gradle clean` | +| `ant -projecthelp` | `gradle tasks` | + +### Information Commands + +| Old Command (Ant) | New Command (Gradle) | +|-------------------------------------------|---------------------------------------------------| +| N/A | `gradle info` | +| N/A | `gradle listVersions` | +| N/A | `gradle listReleases` | +| N/A | `gradle verify` | +| N/A | `gradle validateProperties` | + +### Interactive vs Non-Interactive + +**Old (Ant):** +```bash +# Interactive only +ant release +# Prompted for version input +``` + +**New (Gradle):** +```bash +# Interactive - shows available versions +gradle release + +# Non-interactive - builds specific version +gradle release -PbundleVersion=1.6.39 +``` + +## Configuration Changes + +### build.properties + +**No changes required** - The file format remains the same: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +### gradle.properties + +**Enhanced** with performance optimizations: + +```properties +# Old (minimal) +# (empty or basic settings) + +# New (optimized) +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true +org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m +org.gradle.console=rich +org.gradle.warning.mode=all +org.gradle.vfs.watch=true +``` + +### releases.properties + +**No changes required** - The file format remains the same. + +## Benefits + +### Performance + +| Benefit | Impact | +|------------------------------|--------------------------------------------------| +| Native Gradle tasks | Faster execution | +| Build caching | Reuse previous build outputs | +| Parallel execution | Better multi-core utilization | +| Daemon mode | Reduced startup time | +| Incremental builds | Only rebuild what changed | + +### Maintainability + +| Benefit | Impact | +|------------------------------|--------------------------------------------------| +| Pure Gradle | Simpler codebase | +| Self-contained | No external dependencies | +| Better documentation | Easier to understand and modify | +| Modern tooling | Better IDE support | + +### User Experience + +| Benefit | Impact | +|------------------------------|--------------------------------------------------| +| Better output formatting | Clearer feedback | +| More informative messages | Easier troubleshooting | +| Comprehensive help | Better discoverability | +| Verification tasks | Catch issues early | + +## Migration Steps + +### For Users + +If you're using the build system, follow these steps: + +#### Step 1: Update Repository + +```bash +# Pull latest changes +git pull origin main + +# Verify files +dir +``` + +**Expected files:** +- `build.gradle` (updated) +- `settings.gradle` (updated) +- `gradle.properties` (updated) +- `.gradle-docs/` (new) +- `build.xml` (removed) + +#### Step 2: Verify Environment + +```bash +# Verify build environment +gradle verify +``` + +**Expected output:** +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Build Environment Verification ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Environment Check Results: +──────────────────────────────────────────────────────────────────────────── + ✓ PASS Java 8+ + ✓ PASS build.gradle + ✓ PASS build.properties + ✓ PASS releases.properties + ✓ PASS settings.gradle + ✓ PASS bin/ directory + ✓ PASS 7z command +──────────────────────────────────────────────────────────────────────────── + +[SUCCESS] All checks passed! Build environment is ready. +``` + +#### Step 3: Update Commands + +Replace old Ant commands with new Gradle commands: + +**Old:** +```bash +ant release -Dinput.bundle=1.6.39 +``` + +**New:** +```bash +gradle release -PbundleVersion=1.6.39 +``` + +#### Step 4: Learn New Features + +Explore new tasks: + +```bash +# Display build information +gradle info + +# List available versions +gradle listVersions + +# List all releases +gradle listReleases + +# List all tasks +gradle tasks +``` + +#### Step 5: Read Documentation + +Read the new documentation: + +```bash +# Open documentation +start .gradle-docs/README.md +``` + +Or browse online: +- [README.md](.gradle-docs/README.md) +- [TASKS.md](.gradle-docs/TASKS.md) +- [CONFIGURATION.md](.gradle-docs/CONFIGURATION.md) +- [RELEASE-PROCESS.md](.gradle-docs/RELEASE-PROCESS.md) + +### For Developers + +If you're modifying the build system: + +#### Step 1: Understand New Structure + +Review the new build structure: + +``` +module-memcached/ +├── build.gradle # Pure Gradle build script +├── settings.gradle # Gradle settings +├── gradle.properties # Gradle configuration +├── build.properties # Bundle configuration +├── releases.properties # Release history +└── .gradle-docs/ # Documentation + ├── README.md + ├── TASKS.md + ├── CONFIGURATION.md + ├── RELEASE-PROCESS.md + ├── MIGRATION-GUIDE.md + └── INDEX.md +``` + +#### Step 2: Review Build Script + +Study the new `build.gradle`: + +- Pure Gradle implementation +- No Ant imports +- Native task definitions +- Enhanced output formatting +- Comprehensive error handling + +#### Step 3: Test Changes + +Test your changes: + +```bash +# Verify environment +gradle verify + +# Test build +gradle release -PbundleVersion=1.6.39 + +# Clean up +gradle clean +``` + +#### Step 4: Update Documentation + +Update documentation if needed: + +- Update relevant `.gradle-docs/*.md` files +- Update version and date +- Test all examples +- Update index if structure changes + +### For CI/CD + +If you're using CI/CD pipelines: + +#### Step 1: Update Pipeline Scripts + +**Old (Ant):** +```yaml +- name: Build Release + run: ant release -Dinput.bundle=1.6.39 +``` + +**New (Gradle):** +```yaml +- name: Build Release + run: gradle release -PbundleVersion=1.6.39 +``` + +#### Step 2: Update Environment + +Ensure CI/CD environment has: + +- Java 8+ +- Gradle 7.0+ +- 7-Zip (for 7z format) + +#### Step 3: Update Caching + +Leverage Gradle caching: + +```yaml +- name: Cache Gradle + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} +``` + +#### Step 4: Test Pipeline + +Test the updated pipeline: + +```bash +# Run pipeline locally (if possible) +# Or trigger test build on CI/CD +``` + +## Troubleshooting + +### Common Issues + +#### Issue: build.xml not found + +**Error:** +``` +build.xml not found +``` + +**Cause:** Trying to use old Ant commands + +**Solution:** Use new Gradle commands: +```bash +# Old +ant release -Dinput.bundle=1.6.39 + +# New +gradle release -PbundleVersion=1.6.39 +``` + +--- + +#### Issue: Task not found + +**Error:** +``` +Task 'ant-release' not found +``` + +**Cause:** Trying to use old Ant task names + +**Solution:** Use new Gradle task names: +```bash +# List available tasks +gradle tasks + +# Use correct task name +gradle release -PbundleVersion=1.6.39 +``` + +--- + +#### Issue: Property not recognized + +**Error:** +``` +Property 'input.bundle' not recognized +``` + +**Cause:** Using old Ant property names + +**Solution:** Use new Gradle property names: +```bash +# Old +-Dinput.bundle=1.6.39 + +# New +-PbundleVersion=1.6.39 +``` + +--- + +#### Issue: Build fails with "dev path not found" + +**Error:** +``` +Dev path not found: E:/Bearsampp-development/dev +``` + +**Cause:** Old build script looking for external Ant files + +**Solution:** Update to latest build.gradle: +```bash +git pull origin main +``` + +--- + +### Getting Help + +If you encounter issues: + +1. **Read Documentation** + - [README.md](.gradle-docs/README.md) + - [TASKS.md](.gradle-docs/TASKS.md) + - [CONFIGURATION.md](.gradle-docs/CONFIGURATION.md) + +2. **Check Troubleshooting** + - [README.md - Troubleshooting](.gradle-docs/README.md#troubleshooting) + - [CONFIGURATION.md - Troubleshooting](.gradle-docs/CONFIGURATION.md#troubleshooting-configuration) + - [RELEASE-PROCESS.md - Troubleshooting](.gradle-docs/RELEASE-PROCESS.md#troubleshooting) + +3. **Verify Environment** + ```bash + gradle verify + ``` + +4. **Run with Debug** + ```bash + gradle release -PbundleVersion=1.6.39 --info + gradle release -PbundleVersion=1.6.39 --debug + ``` + +5. **Report Issues** + - [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) + +## Comparison + +### Side-by-Side Comparison + +#### Building a Release + +**Old (Ant):** +```bash +# Interactive only +ant release +# Enter version when prompted: 1.6.39 + +# Or with property +ant release -Dinput.bundle=1.6.39 +``` + +**New (Gradle):** +```bash +# Interactive - shows available versions +gradle release + +# Non-interactive - builds specific version +gradle release -PbundleVersion=1.6.39 +``` + +#### Listing Information + +**Old (Ant):** +```bash +# No built-in commands +# Manual inspection required +dir bin +type releases.properties +``` + +**New (Gradle):** +```bash +# List available versions +gradle listVersions + +# List all releases +gradle listReleases + +# Display build info +gradle info +``` + +#### Verification + +**Old (Ant):** +```bash +# No built-in verification +# Manual checks required +``` + +**New (Gradle):** +```bash +# Verify environment +gradle verify + +# Validate properties +gradle validateProperties +``` + +### Feature Comparison + +| Feature | Ant/Gradle Hybrid | Pure Gradle | +|------------------------------|------------------------|------------------------| +| Build Speed | Moderate | Fast | +| Caching | Limited | Full | +| Parallel Execution | No | Yes | +| Incremental Builds | No | Yes | +| Self-Contained | No | Yes | +| Documentation | Basic | Comprehensive | +| Verification | No | Yes | +| Output Formatting | Basic | Enhanced | +| Error Messages | Basic | Detailed | +| IDE Support | Limited | Full | + +## Best Practices + +### After Migration + +1. **Update Bookmarks** + - Update any documentation bookmarks + - Update command references + - Update CI/CD scripts + +2. **Train Team** + - Share new documentation + - Demonstrate new commands + - Explain benefits + +3. **Monitor** + - Watch for issues + - Gather feedback + - Improve documentation + +4. **Optimize** + - Tune Gradle settings + - Leverage caching + - Use parallel execution + +## Rollback + +If you need to rollback to the old system: + +```bash +# Checkout previous version +git checkout + +# Or revert changes +git revert +``` + +**Note:** Rollback is not recommended as the new system provides significant improvements. + +## Feedback + +We welcome feedback on the migration: + +- Report issues: [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) +- Suggest improvements: [GitHub Discussions](https://github.com/bearsampp/bearsampp/discussions) +- Contribute: [Contributing Guide](../README.md#contributing) + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/README.md b/.gradle-docs/README.md new file mode 100644 index 0000000..52079ea --- /dev/null +++ b/.gradle-docs/README.md @@ -0,0 +1,413 @@ +# Bearsampp Module Memcached - Gradle Build Documentation + +

+ +[![GitHub release](https://img.shields.io/github/release/bearsampp/module-memcached.svg?style=flat-square)](https://github.com/bearsampp/module-memcached/releases/latest) +![Total downloads](https://img.shields.io/github/downloads/bearsampp/module-memcached/total.svg?style=flat-square) + +This is a module of [Bearsampp project](https://github.com/bearsampp/bearsampp) involving Memcached. + +## Table of Contents + +- [Overview](#overview) +- [Quick Start](#quick-start) +- [Build System](#build-system) +- [Available Tasks](#available-tasks) +- [Configuration](#configuration) +- [Release Process](#release-process) +- [Troubleshooting](#troubleshooting) +- [Contributing](#contributing) + +## Overview + +This module provides Memcached binaries for the Bearsampp development environment. The build system uses pure Gradle for packaging and releasing different versions of Memcached. + +### Key Features + +- **Pure Gradle Build** - Modern, maintainable build system (Groovy DSL) +- **No Wrapper Required** - Uses system-installed Gradle +- **Version Management** - Easy management of multiple Memcached versions +- **Automated Packaging** - Automated 7z archive creation +- **Build Verification** - Comprehensive environment checks +- **Documentation** - Complete build documentation + +## Quick Start + +### Prerequisites + +| Requirement | Version | Description | +|---------------------|--------------|------------------------------------------------| +| Java | 8+ | Required for Gradle | +| Gradle | 7.0+ | Build automation tool | +| 7-Zip | Latest | For creating .7z archives | + +### Basic Commands + +```bash +# Display build information +gradle info + +# List available versions +gradle listVersions + +# Build a specific version +gradle release -PbundleVersion=1.6.39 + +# Clean build artifacts +gradle clean + +# Verify build environment +gradle verify +``` + +## Build System + +### Architecture + +The build system is organized as follows: + +``` +module-memcached/ +├── bin/ # Memcached version binaries +│ ├── memcached1.6.15/ +│ ├── memcached1.6.39/ +│ └── ... +├── .gradle-docs/ # Build documentation +│ ├── README.md +│ ├── TASKS.md +│ ├── CONFIGURATION.md +│ └── RELEASE-PROCESS.md +├── build.gradle # Main build script +├── settings.gradle # Gradle settings +├── build.properties # Bundle configuration +├── releases.properties # Release history +└── gradle.properties # Gradle properties +``` + +### Build Flow + +``` +┌─────────────────┐ +│ User Command │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Load Config │ ← build.properties +└────────┬────────┘ releases.properties + │ + ▼ +┌─────────────────┐ +│ Verify Env │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Prepare Files │ ← bin/memcached{version}/ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Create Archive │ → release/bearsampp-memcached-{version}-{date}.7z +└─────────────────┘ +``` + +## Available Tasks + +### Build Tasks + +| Task | Description | Example | +|--------------------|------------------------------------------------|----------------------------------------------| +| `release` | Build release package | `gradle release -PbundleVersion=1.6.39` | +| `clean` | Clean build artifacts | `gradle clean` | + +### Verification Tasks + +| Task | Description | Example | +|------------------------|--------------------------------------------|---------------------------------| +| `verify` | Verify build environment | `gradle verify` | +| `validateProperties` | Validate build.properties | `gradle validateProperties` | + +### Help Tasks + +| Task | Description | Example | +|------------------|------------------------------------------------|----------------------------| +| `info` | Display build information | `gradle info` | +| `listVersions` | List available bundle versions | `gradle listVersions` | +| `listReleases` | List all releases from releases.properties | `gradle listReleases` | +| `tasks` | List all available tasks | `gradle tasks` | + +### Documentation Tasks + +| Task | Description | Example | +|------------------|------------------------------------------------|----------------------------| +| `generateDocs` | Generate build documentation | `gradle generateDocs` | + +## Configuration + +### build.properties + +Main configuration file for the bundle: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +| Property | Description | Default | +|------------------|----------------------------------|----------------| +| `bundle.name` | Bundle name | `memcached` | +| `bundle.release` | Release date | `2025.8.20` | +| `bundle.type` | Bundle type | `bins` | +| `bundle.format` | Archive format | `7z` | + +### gradle.properties + +Gradle-specific configuration: + +```properties +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true +``` + +### releases.properties + +Historical release information: + +```properties +1.6.39 = https://github.com/Bearsampp/module-memcached/releases/download/2025.8.20/bearsampp-memcached-1.6.39-2025.8.20.7z +``` + +## Release Process + +### Step-by-Step Guide + +#### 1. Prepare Version + +Ensure the version exists in `bin/` directory: + +```bash +bin/ +└── memcached1.6.39/ + ├── memcached.exe + └── ... +``` + +#### 2. Update Configuration + +Update `build.properties` with the release date: + +```properties +bundle.release = 2025.8.20 +``` + +#### 3. Verify Environment + +```bash +gradle verify +``` + +Expected output: + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Build Environment Verification ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Environment Check Results: +──────────────────────────────────────────────────────────────────────────── + ✓ PASS Java 8+ + ✓ PASS build.gradle + ✓ PASS build.properties + ✓ PASS releases.properties + ✓ PASS settings.gradle + ✓ PASS bin/ directory + ✓ PASS 7z command +──────────────────────────────────────────────────────────────────────────── + +[SUCCESS] All checks passed! Build environment is ready. +``` + +#### 4. Build Release + +```bash +gradle release -PbundleVersion=1.6.39 +``` + +Expected output: + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Release Build ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Building release for memcached version 1.6.39... + +Bundle path: E:/Bearsampp-development/module-memcached/bin/memcached1.6.39 + +Preparing bundle... +Copying bundle files... +Creating archive: bearsampp-memcached-1.6.39-2025.8.20.7z + +╔════════════════════════════════════════════════════════════════════════════╗ +║ Release Build Completed ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Release package created: + C:\Users\troy\Bearsampp-build\release\bearsampp-memcached-1.6.39-2025.8.20.7z + +Package size: 0.45 MB +``` + +#### 5. Update releases.properties + +Add the new release entry: + +```properties +1.6.39 = https://github.com/Bearsampp/module-memcached/releases/download/2025.8.20/bearsampp-memcached-1.6.39-2025.8.20.7z +``` + +#### 6. Commit and Tag + +```bash +git add . +git commit -m "Release memcached 1.6.39" +git tag -a 2025.8.20 -m "Release 2025.8.20" +git push origin main --tags +``` + +## Troubleshooting + +### Common Issues + +#### Issue: 7z command not found + +**Solution:** + +Install 7-Zip and add it to your PATH: + +```bash +# Windows +set PATH=%PATH%;C:\Program Files\7-Zip +``` + +#### Issue: Bundle version not found + +**Error:** + +``` +Bundle version not found: E:/Bearsampp-development/module-memcached/bin/memcached1.6.39 +``` + +**Solution:** + +1. Check available versions: + ```bash + gradle listVersions + ``` + +2. Ensure the version directory exists in `bin/` + +#### Issue: Java version too old + +**Error:** + +``` +✗ FAIL Java 8+ +``` + +**Solution:** + +Update Java to version 8 or higher: + +```bash +java -version +``` + +### Debug Mode + +Run Gradle with debug output: + +```bash +gradle release -PbundleVersion=1.6.39 --info +gradle release -PbundleVersion=1.6.39 --debug +``` + +### Clean Build + +If you encounter issues, try a clean build: + +```bash +gradle clean +gradle release -PbundleVersion=1.6.39 +``` + +## Contributing + +### Development Workflow + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test the build: + ```bash + gradle verify + gradle release -PbundleVersion=1.6.39 + ``` +5. Submit a pull request + +### Code Style + +- Use 4 spaces for indentation +- Follow Gradle best practices +- Add comments for complex logic +- Update documentation for new features + +### Testing + +Before submitting changes: + +```bash +# Verify environment +gradle verify + +# Validate properties +gradle validateProperties + +# Test release build +gradle release -PbundleVersion=1.6.39 + +# Clean up +gradle clean +``` + +## Additional Resources + +### Documentation + +- [Tasks Reference](TASKS.md) - Detailed task documentation +- [Configuration Guide](CONFIGURATION.md) - Configuration options +- [Release Process](RELEASE-PROCESS.md) - Detailed release guide + +### External Links + +- [Bearsampp Project](https://github.com/bearsampp/bearsampp) +- [Bearsampp Website](https://bearsampp.com) +- [Memcached Official](https://memcached.org) +- [Gradle Documentation](https://docs.gradle.org) + +### Support + +- **Issues:** [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) +- **Website:** [bearsampp.com](https://bearsampp.com) +- **Documentation:** [bearsampp.com/module/memcached](https://bearsampp.com/module/memcached) + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/RELEASE-PROCESS.md b/.gradle-docs/RELEASE-PROCESS.md new file mode 100644 index 0000000..ab5456a --- /dev/null +++ b/.gradle-docs/RELEASE-PROCESS.md @@ -0,0 +1,799 @@ +# Release Process Guide + +Complete guide for creating and publishing releases of Bearsampp Module Memcached. + +## Table of Contents + +- [Overview](#overview) +- [Prerequisites](#prerequisites) +- [Release Workflow](#release-workflow) +- [Step-by-Step Guide](#step-by-step-guide) +- [Version Management](#version-management) +- [Publishing Releases](#publishing-releases) +- [Post-Release Tasks](#post-release-tasks) +- [Troubleshooting](#troubleshooting) + +## Overview + +The release process involves: + +1. **Preparation** - Verify environment and version +2. **Building** - Create release package +3. **Testing** - Verify package integrity +4. **Publishing** - Upload to GitHub releases +5. **Documentation** - Update release notes and properties + +### Release Timeline + +``` +┌─────────────┐ +│ Preparation │ (15 min) +└──────┬──────┘ + │ + ▼ +┌─��───────────┐ +│ Building │ (5 min) +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ Testing │ (10 min) +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ Publishing │ (10 min) +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ Done │ +└─────────────┘ + +Total: ~40 minutes +``` + +--- + +## Prerequisites + +### Required Tools + +| Tool | Version | Purpose | Installation | +|------------------|--------------|--------------------------------------------|-----------------------------------------| +| Java | 8+ | Run Gradle build | [Download](https://adoptium.net/) | +| Gradle | 7.0+ | Build automation | [Download](https://gradle.org/) | +| 7-Zip | Latest | Create .7z archives | [Download](https://www.7-zip.org/) | +| Git | Latest | Version control | [Download](https://git-scm.com/) | + +### Required Access + +| Access Type | Purpose | Required For | +|---------------------|--------------------------------------------|-----------------------------------------| +| GitHub Write | Push commits and tags | Publishing releases | +| GitHub Releases | Create and upload releases | Publishing releases | + +### Environment Setup + +```bash +# Verify Java +java -version + +# Verify Gradle +gradle --version + +# Verify 7-Zip +7z + +# Verify Git +git --version + +# Verify build environment +gradle verify +``` + +**Expected Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Build Environment Verification ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Environment Check Results: +──────────────────────────────────���──────────────────────────────────��────── + ✓ PASS Java 8+ + ✓ PASS build.gradle + ✓ PASS build.properties + ✓ PASS releases.properties + ✓ PASS settings.gradle + ✓ PASS bin/ directory + ✓ PASS 7z command +──────────────────────────────────────────────────────────────────────────── + +[SUCCESS] All checks passed! Build environment is ready. +``` + +--- + +## Release Workflow + +### Standard Release Flow + +``` +1. Check for new Memcached version + ↓ +2. Download and prepare binaries + ↓ +3. Add to bin/ directory + ↓ +4. Update build.properties + ↓ +5. Build release package + ↓ +6. Test package + ↓ +7. Create Git tag + ↓ +8. Push to GitHub + ↓ +9. Create GitHub release + ↓ +10. Upload package + ↓ +11. Update releases.properties + ↓ +12. Commit and push +``` + +--- + +## Step-by-Step Guide + +### Step 1: Check for New Version + +Check Memcached official releases: + +**Source:** https://memcached.org/downloads + +**Example:** +``` +Latest version: 1.6.40 +Current version: 1.6.39 +Action: Proceed with release +``` + +--- + +### Step 2: Download Binaries + +Download Windows binaries for the new version. + +**Sources:** +- Official Memcached builds +- Third-party Windows builds +- Compiled from source + +**Verify:** +- `memcached.exe` is present +- Version matches expected +- All dependencies included + +--- + +### Step 3: Prepare Binary Directory + +Create version directory in `bin/`: + +```bash +# Create directory +mkdir bin/memcached1.6.40 + +# Copy binaries +copy memcached.exe bin/memcached1.6.40/ +copy *.dll bin/memcached1.6.40/ + +# Verify +dir bin/memcached1.6.40 +``` + +**Expected Structure:** +``` +bin/memcached1.6.40/ +├── memcached.exe +├── pthreadGC2.dll +└── README.txt (optional) +``` + +--- + +### Step 4: Update Configuration + +Update `build.properties` with new release date: + +```properties +bundle.name = memcached +bundle.release = 2025.9.15 +bundle.type = bins +bundle.format = 7z +``` + +**Release Date Format:** `YYYY.M.D` or `YYYY.MM.DD` + +**Examples:** +- `2025.9.15` - September 15, 2025 +- `2025.12.1` - December 1, 2025 + +--- + +### Step 5: Verify Configuration + +```bash +# Validate properties +gradle validateProperties + +# List available versions +gradle listVersions + +# Verify environment +gradle verify +``` + +**Expected Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Validating build.properties ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +[SUCCESS] All required properties are present: + +Property Value +────────────────────────────────────────────────────── +bundle.name memcached +bundle.release 2025.9.15 +bundle.type bins +bundle.format 7z +────────────────────────────────────────────────────── +``` + +--- + +### Step 6: Build Release Package + +Build the release package: + +```bash +gradle release -PbundleVersion=1.6.40 +``` + +**Expected Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Release Build ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Building release for memcached version 1.6.40... + +Bundle path: E:/Bearsampp-development/module-memcached/bin/memcached1.6.40 + +Preparing bundle... +Copying bundle files... +Creating archive: bearsampp-memcached-1.6.40-2025.9.15.7z + +╔════════════════════════════════════════════════════════════════════════════╗ +║ Release Build Completed ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Release package created: + C:\Users\troy\Bearsampp-build\release\bearsampp-memcached-1.6.40-2025.9.15.7z + +Package size: 0.45 MB +``` + +**Output Location:** +``` +${buildPath}/release/bearsampp-memcached-1.6.40-2025.9.15.7z +``` + +--- + +### Step 7: Test Package + +Verify the release package: + +```bash +# Extract to test directory +7z x bearsampp-memcached-1.6.40-2025.9.15.7z -otest/ + +# Verify contents +dir test/memcached1.6.40 + +# Test executable +test/memcached1.6.40/memcached.exe -h +``` + +**Verification Checklist:** + +- [ ] Archive extracts without errors +- [ ] All files present +- [ ] `memcached.exe` runs +- [ ] Version is correct +- [ ] No missing dependencies + +--- + +### Step 8: Create Git Tag + +Create and push Git tag: + +```bash +# Stage changes +git add bin/memcached1.6.40/ +git add build.properties + +# Commit +git commit -m "Add memcached 1.6.40" + +# Create tag +git tag -a 2025.9.15 -m "Release 2025.9.15 - Memcached 1.6.40" + +# Push commit +git push origin main + +# Push tag +git push origin 2025.9.15 +``` + +**Tag Format:** `YYYY.M.D` or `YYYY.MM.DD` + +**Tag Message Format:** +``` +Release YYYY.M.D - Memcached X.X.X + +- Add memcached X.X.X +- Update build configuration +- Update documentation +``` + +--- + +### Step 9: Create GitHub Release + +Create release on GitHub: + +**URL:** https://github.com/bearsampp/module-memcached/releases/new + +**Release Form:** + +| Field | Value | +|---------------------|----------------------------------------------------------------| +| Tag | `2025.9.15` | +| Release Title | `Memcached 1.6.40 - 2025.9.15` | +| Description | See template below | +| Attach Files | `bearsampp-memcached-1.6.40-2025.9.15.7z` | + +**Release Description Template:** + +```markdown +# Memcached 1.6.40 - Release 2025.9.15 + +## What's New + +- Updated to Memcached 1.6.40 +- Latest Windows binaries +- Compatible with Bearsampp 2025.x + +## Download + +- **File:** bearsampp-memcached-1.6.40-2025.9.15.7z +- **Size:** 0.45 MB +- **Format:** 7z + +## Installation + +1. Download the archive +2. Extract to Bearsampp modules directory +3. Restart Bearsampp + +## Changelog + +### Memcached 1.6.40 + +- Bug fixes and improvements +- Performance enhancements +- Security updates + +## Requirements + +- Bearsampp 2025.x or later +- Windows 10/11 (64-bit) + +## Links + +- [Memcached Official](https://memcached.org/) +- [Bearsampp Project](https://github.com/bearsampp/bearsampp) +- [Documentation](https://bearsampp.com/module/memcached) + +## Support + +Report issues at: https://github.com/bearsampp/bearsampp/issues +``` + +--- + +### Step 10: Upload Package + +Upload the release package to GitHub: + +1. Go to the release page +2. Click "Edit release" +3. Drag and drop `bearsampp-memcached-1.6.40-2025.9.15.7z` +4. Wait for upload to complete +5. Click "Update release" + +**Verify Upload:** +- File appears in release assets +- Download link works +- File size is correct + +--- + +### Step 11: Update releases.properties + +Add new release entry: + +```properties +# releases.properties + +# ... existing entries ... + +1.6.40 = https://github.com/Bearsampp/module-memcached/releases/download/2025.9.15/bearsampp-memcached-1.6.40-2025.9.15.7z +``` + +**URL Format:** +``` +https://github.com/Bearsampp/module-memcached/releases/download/{tag}/bearsampp-memcached-{version}-{release}.7z +``` + +**Sort Order:** Ascending by version number + +--- + +### Step 12: Commit and Push + +Commit the updated releases.properties: + +```bash +# Stage changes +git add releases.properties + +# Commit +git commit -m "Update releases.properties for 1.6.40" + +# Push +git push origin main +``` + +--- + +### Step 13: Verify Release + +Verify the release is complete: + +```bash +# List releases +gradle listReleases + +# Check latest release +curl -s https://api.github.com/repos/bearsampp/module-memcached/releases/latest | grep tag_name +``` + +**Verification Checklist:** + +- [ ] Release appears on GitHub +- [ ] Package is downloadable +- [ ] releases.properties is updated +- [ ] Git tag exists +- [ ] Documentation is updated + +--- + +## Version Management + +### Version Numbering + +Memcached uses semantic versioning: + +``` +MAJOR.MINOR.PATCH + │ │ │ + │ │ └─── Bug fixes + │ └───────── New features (backward compatible) + └─────────────── Breaking changes +``` + +**Examples:** +- `1.6.39` - Version 1, minor 6, patch 39 +- `1.6.40` - Version 1, minor 6, patch 40 +- `2.0.0` - Version 2, minor 0, patch 0 + +### Release Numbering + +Bearsampp releases use date-based versioning: + +``` +YYYY.M.D + │ │ │ + │ │ └─── Day + │ └───── Month + └───────── Year +``` + +**Examples:** +- `2025.9.15` - September 15, 2025 +- `2025.12.1` - December 1, 2025 +- `2026.1.20` - January 20, 2026 + +### Version Matrix + +| Memcached Version | Release Date | Bearsampp Release | Status | +|------------------|--------------|-------------------|-------------| +| 1.6.39 | 2025.8.20 | 2025.8.20 | Current | +| 1.6.38 | 2025.4.19 | 2025.4.19 | Supported | +| 1.6.36 | 2025.2.11 | 2025.2.11 | Supported | +| 1.6.33 | 2024.12.23 | 2024.12.23 | Supported | +| 1.6.32 | 2024.12.1 | 2024.12.1 | Supported | + +--- + +## Publishing Releases + +### GitHub Release Checklist + +- [ ] Tag created and pushed +- [ ] Release created on GitHub +- [ ] Release title follows format +- [ ] Release description is complete +- [ ] Package uploaded +- [ ] Package is downloadable +- [ ] releases.properties updated +- [ ] Documentation updated + +### Release Assets + +Each release should include: + +| Asset | Type | Required | Description | +|-----------------------------------------------|----------|----------|--------------------------------| +| `bearsampp-memcached-{version}-{date}.7z` | Binary | Yes | Release package | +| Release notes | Text | Yes | Changelog and information | + +### Release Metadata + +GitHub release metadata: + +```json +{ + "tag_name": "2025.9.15", + "name": "Memcached 1.6.40 - 2025.9.15", + "draft": false, + "prerelease": false, + "body": "Release description...", + "assets": [ + { + "name": "bearsampp-memcached-1.6.40-2025.9.15.7z", + "content_type": "application/x-7z-compressed" + } + ] +} +``` + +--- + +## Post-Release Tasks + +### Update Documentation + +Update project documentation: + +1. **README.md** - Update version badges +2. **.gradle-docs/** - Update documentation +3. **CHANGELOG.md** - Add release notes (if exists) + +### Announce Release + +Announce the release: + +1. **GitHub Discussions** - Post announcement +2. **Project Website** - Update downloads page +3. **Social Media** - Share release (if applicable) + +### Monitor Issues + +Monitor for issues: + +1. Check GitHub issues +2. Monitor download statistics +3. Review user feedback + +--- + +## Troubleshooting + +### Common Issues + +#### Issue: Build fails + +**Error:** +``` +Bundle version not found: E:/Bearsampp-development/module-memcached/bin/memcached1.6.40 +``` + +**Solution:** +1. Verify directory exists: `dir bin/memcached1.6.40` +2. Check directory name matches version +3. Ensure binaries are present + +--- + +#### Issue: 7z compression fails + +**Error:** +``` +7z compression failed with exit code: 2 +``` + +**Solution:** +1. Verify 7z is installed: `7z` +2. Check PATH includes 7-Zip +3. Verify disk space available +4. Check file permissions + +--- + +#### Issue: Git push fails + +**Error:** +``` +! [rejected] main -> main (fetch first) +``` + +**Solution:** +```bash +# Pull latest changes +git pull origin main + +# Resolve conflicts if any +git mergetool + +# Push again +git push origin main +``` + +--- + +#### Issue: GitHub release upload fails + +**Error:** +``` +Failed to upload asset +``` + +**Solution:** +1. Check file size (< 2GB) +2. Verify internet connection +3. Try uploading via web interface +4. Check GitHub status + +--- + +#### Issue: Package extraction fails + +**Error:** +``` +Cannot open file as archive +``` + +**Solution:** +1. Verify package integrity +2. Re-download package +3. Rebuild package +4. Check 7z version + +--- + +### Debug Commands + +```bash +# Verify build +gradle verify --info + +# Test build +gradle release -PbundleVersion=1.6.40 --debug + +# Check Git status +git status +git log --oneline -5 + +# Verify package +7z t bearsampp-memcached-1.6.40-2025.9.15.7z + +# Test extraction +7z x bearsampp-memcached-1.6.40-2025.9.15.7z -otest/ +``` + +--- + +## Release Checklist + +### Pre-Release + +- [ ] New version available +- [ ] Binaries downloaded +- [ ] Directory created in `bin/` +- [ ] `build.properties` updated +- [ ] Environment verified +- [ ] Configuration validated + +### Build + +- [ ] Release package built +- [ ] Package tested +- [ ] Package size verified +- [ ] Contents verified + +### Publish + +- [ ] Git changes committed +- [ ] Git tag created +- [ ] Changes pushed to GitHub +- [ ] GitHub release created +- [ ] Package uploaded +- [ ] Release published + +### Post-Release + +- [ ] `releases.properties` updated +- [ ] Changes committed and pushed +- [ ] Release verified +- [ ] Documentation updated +- [ ] Announcement posted + +--- + +## Best Practices + +### Version Control + +- Always create tags for releases +- Use descriptive commit messages +- Keep release history in `releases.properties` +- Document breaking changes + +### Testing + +- Test package before publishing +- Verify all files are included +- Check executable runs correctly +- Test on clean system if possible + +### Documentation + +- Keep documentation up-to-date +- Document known issues +- Provide clear installation instructions +- Include changelog + +### Communication + +- Announce releases clearly +- Respond to user feedback +- Monitor for issues +- Provide support + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/TASKS.md b/.gradle-docs/TASKS.md new file mode 100644 index 0000000..cf02d78 --- /dev/null +++ b/.gradle-docs/TASKS.md @@ -0,0 +1,594 @@ +# Gradle Tasks Reference + +Complete reference for all available Gradle tasks in the Bearsampp Module Memcached build system. + +## Table of Contents + +- [Build Tasks](#build-tasks) +- [Verification Tasks](#verification-tasks) +- [Help Tasks](#help-tasks) +- [Documentation Tasks](#documentation-tasks) +- [Task Dependencies](#task-dependencies) +- [Custom Properties](#custom-properties) + +## Build Tasks + +### release + +Build a release package for a specific Memcached version. + +**Group:** `build` + +**Syntax:** + +```bash +# Interactive mode (lists available versions) +gradle release + +# Non-interactive mode (builds specific version) +gradle release -PbundleVersion= +``` + +**Parameters:** + +| Parameter | Type | Required | Description | Example | +|-------------------|----------|----------|---------------------------------------|--------------| +| `bundleVersion` | String | No | Version to build (e.g., "1.6.39") | `1.6.39` | + +**Examples:** + +```bash +# Interactive mode - shows available versions +gradle release + +# Build version 1.6.39 +gradle release -PbundleVersion=1.6.39 + +# Build version 1.6.38 +gradle release -PbundleVersion=1.6.38 +``` + +**Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Release Build ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Building release for memcached version 1.6.39... + +Bundle path: E:/Bearsampp-development/module-memcached/bin/memcached1.6.39 + +Preparing bundle... +Copying bundle files... +Creating archive: bearsampp-memcached-1.6.39-2025.8.20.7z + +╔════════════════════════════════════════════════════════════════════════════╗ +║ Release Build Completed ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Release package created: + C:\Users\troy\Bearsampp-build\release\bearsampp-memcached-1.6.39-2025.8.20.7z + +Package size: 0.45 MB +``` + +**Process:** + +1. Validates the specified version exists in `bin/` directory +2. Creates temporary build directory +3. Copies bundle files to temporary location +4. Creates 7z archive with proper naming convention +5. Outputs archive location and size + +**Error Handling:** + +- **Version not found:** Lists available versions +- **Missing 7z:** Provides installation instructions +- **Permission errors:** Suggests running with elevated privileges + +--- + +### clean + +Clean build artifacts and temporary files. + +**Group:** `build` + +**Syntax:** + +```bash +gradle clean +``` + +**Examples:** + +```bash +# Clean all build artifacts +gradle clean + +# Clean and rebuild +gradle clean release -PbundleVersion=1.6.39 +``` + +**Output:** + +``` +Cleaned: E:\Bearsampp-development\module-memcached\build +Cleaned: C:\Users\troy\Bearsampp-build\tmp + +[SUCCESS] Build artifacts cleaned +``` + +**Cleaned Directories:** + +| Directory | Description | +|-------------------------------------------|--------------------------------| +| `build/` | Gradle build directory | +| `${buildPath}/tmp/` | Temporary build files | + +--- + +## Verification Tasks + +### verify + +Verify the build environment and dependencies. + +**Group:** `verification` + +**Syntax:** + +```bash +gradle verify +``` + +**Examples:** + +```bash +# Verify build environment +gradle verify + +# Verify with detailed output +gradle verify --info +``` + +**Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Build Environment Verification ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Environment Check Results: +──────────────────────────────────────────────────────────────────────────── + ✓ PASS Java 8+ + ✓ PASS build.gradle + ✓ PASS build.properties + ✓ PASS releases.properties + ✓ PASS settings.gradle + �� PASS bin/ directory + ✓ PASS 7z command +──────────────────────────────────────────────────────────────────────────── + +[SUCCESS] All checks passed! Build environment is ready. + +You can now run: + gradle release -PbundleVersion=1.6.39 - Build specific version + gradle listVersions - List available versions +``` + +**Checks Performed:** + +| Check | Description | Requirement | +|-----------------------|------------------------------------------------|------------------| +| Java 8+ | Java version 8 or higher | Required | +| build.gradle | Main build script exists | Required | +| build.properties | Bundle configuration exists | Required | +| releases.properties | Release history exists | Required | +| settings.gradle | Gradle settings exists | Required | +| bin/ directory | Binary directory exists | Required | +| 7z command | 7-Zip command available | Required | + +--- + +### validateProperties + +Validate build.properties configuration. + +**Group:** `verification` + +**Syntax:** + +```bash +gradle validateProperties +``` + +**Examples:** + +```bash +# Validate properties +gradle validateProperties +``` + +**Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Validating build.properties ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +[SUCCESS] All required properties are present: + +Property Value +────────────────────────────────────────────────── +bundle.name memcached +bundle.release 2025.8.20 +bundle.type bins +bundle.format 7z +────────────────────────────────────────────────── +``` + +**Validated Properties:** + +| Property | Required | Description | +|------------------|----------|---------------------------------------| +| `bundle.name` | Yes | Bundle name (e.g., "memcached") | +| `bundle.release` | Yes | Release date (e.g., "2025.8.20") | +| `bundle.type` | Yes | Bundle type (e.g., "bins") | +| `bundle.format` | Yes | Archive format (e.g., "7z") | + +--- + +## Help Tasks + +### info + +Display build configuration information. + +**Group:** `help` + +**Syntax:** + +```bash +gradle info +``` + +**Examples:** + +```bash +# Display build information +gradle info +``` + +**Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Bearsampp Module Memcached - Build Info ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Project Information: + Name: module-memcached + Version: 2025.8.20 + Description: Bearsampp Module - memcached + Group: com.bearsampp.modules + +Bundle Properties: + Name: memcached + Release: 2025.8.20 + Type: bins + Format: 7z + +Paths: + Project Directory: E:/Bearsampp-development/module-memcached + Root Directory: E:/Bearsampp-development + Build Path: C:\Users\troy\Bearsampp-build + Temp Path: C:\Users\troy\Bearsampp-build\tmp + Release Path: C:\Users\troy\Bearsampp-build\release + +Environment: + Java Version: 17.0.2 + Java Home: C:\Program Files\Java\jdk-17.0.2 + Gradle Version: 8.5 + Gradle Home: C:\Users\troy\.gradle\wrapper\dists\gradle-8.5 + Operating System: Windows 11 10.0 + +Available Task Groups: + • build - Build and package tasks + • verification - Verification and validation tasks + • help - Help and information tasks + • documentation - Documentation tasks + +Quick Start: + gradle tasks - List all available tasks + gradle info - Show this information + gradle release - Interactive release build + gradle release -PbundleVersion=1.6.39 - Non-interactive release + gradle clean - Clean build artifacts + gradle verify - Verify build environment + gradle listVersions - List available bundle versions + gradle listReleases - List all releases + +Documentation: + See .gradle-docs/ for comprehensive build documentation +``` + +--- + +### listVersions + +List all available bundle versions in the bin/ directory. + +**Group:** `help` + +**Syntax:** + +```bash +gradle listVersions +``` + +**Examples:** + +```bash +# List available versions +gradle listVersions +``` + +**Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Available memcached Versions in bin/ ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Version Size Path +──────────────────────────────────────────────────────────────────────────── +1.6.15 0.42 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.15 +1.6.17 0.43 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.17 +1.6.18 0.43 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.18 +1.6.21 0.44 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.21 +1.6.24 0.44 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.24 +1.6.29 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.29 +1.6.31 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.31 +1.6.32 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.32 +1.6.33 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.33 +1.6.36 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.36 +1.6.37 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.37 +1.6.38 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.38 +1.6.39 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.39 +─────────────────────────────────────────────────────���────────────────────── + +Total versions: 13 + +To build a specific version: + gradle release -PbundleVersion=1.6.39 +``` + +--- + +### listReleases + +List all available releases from releases.properties. + +**Group:** `help` + +**Syntax:** + +```bash +gradle listReleases +``` + +**Examples:** + +```bash +# List all releases +gradle listReleases +``` + +**Output:** + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Available Memcached Releases ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +Version Download URL +──────────────────────────────────────────────────────────────────────────── +1.6.6 https://github.com/Bearsampp/module-memcached/releases/download/2022.07.14/bearsampp-memcached-1.6.6-2022.07.14.7z +1.6.15 https://github.com/Bearsampp/module-memcached/releases/download/2022.08.04/bearsampp-memcached-1.6.15-2022.08.04.7z +1.6.17 https://github.com/Bearsampp/module-memcached/releases/download/2022.09.26/bearsampp-memcached-1.6.17-2022.09.24.7z +1.6.18 https://github.com/Bearsampp/module-memcached/releases/download/2023.3.5/bearsampp-memcached-1.6.18-2023.3.5.7z +1.6.21 https://github.com/Bearsampp/module-memcached/releases/download/2023.10.1/bearsampp-memcached-1.6.21-2023.10.1.7z +1.6.24 https://github.com/Bearsampp/module-memcached/releases/download/2024.3.30/bearsampp-memcached-1.6.24-2024.3.30.7z +1.6.29 https://github.com/Bearsampp/module-memcached/releases/download/2024.7.29/bearsampp-memcached-1.6.29-2024.10.7.7z +1.6.31 https://github.com/Bearsampp/module-memcached/releases/download/2024.10.7/bearsampp-memcached-1.6.31-2024.10.7.7z +1.6.32 https://github.com/Bearsampp/module-memcached/releases/download/2024.12.1/bearsampp-memcached-1.6.32-2024.12.1.7z +1.6.33 https://github.com/Bearsampp/module-memcached/releases/download/2024.12.23/bearsampp-memcached-1.6.33-2024.12.23.7z +1.6.36 https://github.com/Bearsampp/module-memcached/releases/download/2025.2.11/bearsampp-memcached-1.6.36-2025.2.11.7z +1.6.38 https://github.com/Bearsampp/module-memcached/releases/download/2025.4.19/bearsampp-memcached-1.6.38-2025.4.19.7z +1.6.39 https://github.com/Bearsampp/module-memcached/releases/download/2025.8.20/bearsampp-memcached-1.6.39-2025.8.20.7z +──────────────────────────────────────────────────────────────────────────── + +Total releases: 13 +``` + +--- + +### tasks + +List all available Gradle tasks. + +**Group:** `help` + +**Syntax:** + +```bash +gradle tasks +gradle tasks --all +``` + +**Examples:** + +```bash +# List main tasks +gradle tasks + +# List all tasks including internal ones +gradle tasks --all +``` + +--- + +## Documentation Tasks + +### generateDocs + +Generate build documentation in .gradle-docs/. + +**Group:** `documentation` + +**Syntax:** + +```bash +gradle generateDocs +``` + +**Examples:** + +```bash +# Generate documentation +gradle generateDocs +``` + +**Output:** + +``` +Generating documentation in .gradle-docs/... +[SUCCESS] Documentation directory created +See .gradle-docs/ for comprehensive build documentation +``` + +--- + +## Task Dependencies + +### Dependency Graph + +``` +release + (no dependencies) + +clean + (no dependencies) + +verify + (no dependencies) + +validateProperties + (no dependencies) + +info + (no dependencies) + +listVersions + (no dependencies) + +listReleases + (no dependencies) + +generateDocs + (no dependencies) +``` + +### Common Task Chains + +```bash +# Clean and build +gradle clean release -PbundleVersion=1.6.39 + +# Verify and build +gradle verify release -PbundleVersion=1.6.39 + +# Validate and build +gradle validateProperties release -PbundleVersion=1.6.39 + +# Full verification and build +gradle clean verify validateProperties release -PbundleVersion=1.6.39 +``` + +--- + +## Custom Properties + +### Project Properties + +Properties that can be passed via command line: + +| Property | Type | Description | Example | +|------------------|----------|---------------------------------------|----------------------------------------------| +| `bundleVersion` | String | Version to build | `-PbundleVersion=1.6.39` | + +### System Properties + +System properties that can be set: + +| Property | Type | Description | Example | +|--------------------------|-----------|----------------------------------|------------------------------------------| +| `org.gradle.daemon` | Boolean | Enable Gradle daemon | `-Dorg.gradle.daemon=true` | +| `org.gradle.parallel` | Boolean | Enable parallel execution | `-Dorg.gradle.parallel=true` | +| `org.gradle.caching` | Boolean | Enable build caching | `-Dorg.gradle.caching=true` | + +### Environment Variables + +Environment variables used by the build: + +| Variable | Description | Example | +|------------------|---------------------------------------|----------------------------------------------| +| `JAVA_HOME` | Java installation directory | `C:\Program Files\Java\jdk-17.0.2` | +| `GRADLE_HOME` | Gradle installation directory | `C:\Gradle\gradle-8.5` | +| `PATH` | System path (must include 7z) | `C:\Program Files\7-Zip;...` | + +--- + +## Task Output Formats + +### Success Output + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ [Task Name] Completed ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +[SUCCESS] Task completed successfully +``` + +### Error Output + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ [Task Name] Failed ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +[ERROR] Error message here + +Possible solutions: + • Solution 1 + • Solution 2 +``` + +### Warning Output + +``` +[WARNING] Warning message here + +Please review: + • Item 1 + • Item 2 +``` + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/BUILD-SYSTEM.md b/BUILD-SYSTEM.md new file mode 100644 index 0000000..0fb9f28 --- /dev/null +++ b/BUILD-SYSTEM.md @@ -0,0 +1,418 @@ +# Build System Specification + +## Overview + +The Bearsampp Module Memcached uses a **pure Gradle build system** with the following specifications: + +## Build System Details + +| Aspect | Specification | +|--------------------------|--------------------------------------------------| +| **Build Tool** | Gradle (system-installed) | +| **Gradle Version** | 7.0+ (tested with 9.2.0) | +| **DSL Language** | Groovy | +| **Wrapper** | Not used (requires system Gradle) | +| **Build Files** | `build.gradle`, `settings.gradle` (Groovy) | +| **Configuration Files** | `build.properties`, `gradle.properties` | + +## File Structure + +### Build Files + +``` +module-memcached/ +├── build.gradle # Main build script (Groovy DSL) +├── settings.gradle # Gradle settings (Groovy DSL) +├── gradle.properties # Gradle configuration +└── build.properties # Bundle configuration +``` + +### No Wrapper Files + +The following files are **NOT** present (no wrapper used): + +- ❌ `gradlew` (Unix wrapper script) +- ❌ `gradlew.bat` (Windows wrapper script) +- ❌ `gradle/wrapper/gradle-wrapper.jar` +- ❌ `gradle/wrapper/gradle-wrapper.properties` + +### No Kotlin Files + +The following files are **NOT** present (Groovy DSL only): + +- ❌ `build.gradle.kts` (Kotlin DSL) +- ❌ `settings.gradle.kts` (Kotlin DSL) +- ❌ Any `.kts` files + +## Groovy DSL Syntax + +### build.gradle (Groovy) + +```groovy +plugins { + id 'base' + id 'distribution' +} + +// Load build properties +def buildProps = new Properties() +file('build.properties').withInputStream { buildProps.load(it) } + +// Project information +group = 'com.bearsampp.modules' +version = buildProps.getProperty('bundle.release', '1.0.0') + +// Define project paths +ext { + projectBasedir = projectDir.absolutePath + bundleName = buildProps.getProperty('bundle.name', 'memcached') +} + +// Task definitions +tasks.register('info') { + group = 'help' + description = 'Display build information' + + doLast { + println "Project: ${project.name}" + } +} +``` + +### settings.gradle (Groovy) + +```groovy +rootProject.name = 'module-memcached' + +enableFeaturePreview('STABLE_CONFIGURATION_CACHE') + +buildCache { + local { + enabled = true + directory = file("${rootDir}/.gradle/build-cache") + } +} +``` + +## Why Groovy DSL? + +### Advantages + +| Advantage | Description | +|--------------------------|--------------------------------------------------| +| **Mature** | Stable, well-established DSL | +| **Flexible** | Dynamic typing, easier scripting | +| **Compatible** | Works with all Gradle versions | +| **Readable** | Cleaner syntax for build scripts | +| **Documentation** | More examples and resources available | + +### Comparison: Groovy vs Kotlin DSL + +| Aspect | Groovy DSL | Kotlin DSL | +|----------------------|---------------------------|---------------------------| +| File Extension | `.gradle` | `.gradle.kts` | +| Syntax | Dynamic, flexible | Static, type-safe | +| IDE Support | Good | Better (IntelliJ) | +| Build Performance | Fast | Slower (compilation) | +| Learning Curve | Easier | Steeper | +| Maturity | Very mature | Newer | + +## System Requirements + +### Required + +| Requirement | Version | Purpose | +|---------------------|--------------|---------------------------------------------| +| Java | 8+ | Run Gradle | +| Gradle | 7.0+ | Build automation (system-installed) | +| 7-Zip | Latest | Create .7z archives | + +### Installation + +#### Install Gradle (System-wide) + +**Windows:** + +```powershell +# Using Chocolatey +choco install gradle + +# Using Scoop +scoop install gradle + +# Manual installation +# 1. Download from https://gradle.org/releases/ +# 2. Extract to C:\Gradle +# 3. Add C:\Gradle\bin to PATH +``` + +**Verify Installation:** + +```bash +gradle --version +``` + +Expected output: + +``` +------------------------------------------------------------ +Gradle 9.2.0 +------------------------------------------------------------ + +Build time: 2025-10-29 13:53:23 UTC +Revision: d9d6bbce03b3d88c67ef5a0ff31f7ae5e332d6bf + +Kotlin: 2.2.20 +Groovy: 4.0.28 +Ant: Apache Ant(TM) version 1.10.15 compiled on August 25 2024 +Launcher JVM: 25.0.1 (Microsoft 25.0.1+8-LTS) +Daemon JVM: C:\Program Files\Microsoft\jdk-25.0.1.8-hotspot +OS: Windows 11 10.0 amd64 +``` + +## Build Commands + +All commands use the system-installed `gradle` command: + +```bash +# Display build information +gradle info + +# List available tasks +gradle tasks + +# Build release +gradle release -PbundleVersion=1.6.39 + +# Clean build artifacts +gradle clean + +# Verify environment +gradle verify +``` + +## Configuration + +### gradle.properties + +Gradle daemon and performance settings: + +```properties +# Enable Gradle daemon for faster builds +org.gradle.daemon=true + +# Enable parallel execution +org.gradle.parallel=true + +# Enable build caching +org.gradle.caching=true + +# Enable configuration on demand +org.gradle.configureondemand=true + +# JVM arguments +org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m + +# Console output +org.gradle.console=rich + +# Warning mode +org.gradle.warning.mode=all + +# File system watching (Gradle 7.0+) +org.gradle.vfs.watch=true +``` + +### build.properties + +Bundle-specific configuration: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +## Build Process + +### 1. Configuration Phase + +``` +gradle command + ↓ +Load settings.gradle (Groovy) + ↓ +Load build.gradle (Groovy) + ↓ +Load build.properties + ↓ +Configure project +``` + +### 2. Execution Phase + +``` +Execute requested task + ↓ +Run task actions + ↓ +Generate output +``` + +## Verification + +### Check Build System + +```bash +# Verify Gradle installation +gradle --version + +# Verify no wrapper files +ls gradlew* 2>$null +ls gradle/wrapper 2>$null + +# Verify Groovy DSL files +ls *.gradle + +# Verify no Kotlin DSL files +ls *.gradle.kts 2>$null +``` + +### Expected Results + +```bash +# gradle --version +✓ Shows Gradle version with Groovy + +# ls gradlew* +✗ No wrapper files found + +# ls *.gradle +✓ build.gradle +✓ settings.gradle + +# ls *.gradle.kts +✗ No Kotlin DSL files found +``` + +## Migration Notes + +### From Ant + +- ✓ Removed `build.xml` +- ✓ Converted to pure Gradle (Groovy DSL) +- ✓ No external Ant dependencies + +### From Gradle Wrapper + +- ✓ No `gradlew` scripts +- ✓ No `gradle/wrapper/` directory +- ✓ Uses system-installed Gradle + +### From Kotlin DSL + +- ✓ No `.gradle.kts` files +- ✓ Uses Groovy DSL (`.gradle` files) +- ✓ Dynamic, flexible syntax + +## Best Practices + +### Groovy DSL + +1. **Use single quotes** for strings (unless interpolation needed) + ```groovy + id 'base' // Good + id "base" // Works but unnecessary + ``` + +2. **Use method call syntax** without parentheses + ```groovy + println 'Hello' // Good + println('Hello') // Works but verbose + ``` + +3. **Use closures** for configuration + ```groovy + tasks.register('myTask') { + doLast { + println 'Task executed' + } + } + ``` + +4. **Use ext for extra properties** + ```groovy + ext { + myProperty = 'value' + } + ``` + +### System Gradle + +1. **Keep Gradle updated** on development machines +2. **Document required Gradle version** in README +3. **Use version-compatible features** (7.0+) +4. **Test with multiple Gradle versions** if possible + +## Troubleshooting + +### Issue: Gradle not found + +**Error:** +``` +'gradle' is not recognized as an internal or external command +``` + +**Solution:** +Install Gradle system-wide and add to PATH. + +### Issue: Wrong Gradle version + +**Error:** +``` +This build requires Gradle 7.0 or higher +``` + +**Solution:** +Update Gradle installation: +```bash +gradle --version +# If < 7.0, update Gradle +``` + +### Issue: Groovy syntax error + +**Error:** +``` +Could not compile build file 'build.gradle' +``` + +**Solution:** +Check Groovy syntax: +- Use single quotes for strings +- Check closure syntax +- Verify method calls + +## Documentation + +For more information, see: + +- [README.md](README.md) - Project overview +- [.gradle-docs/README.md](.gradle-docs/README.md) - Build documentation +- [.gradle-docs/CONFIGURATION.md](.gradle-docs/CONFIGURATION.md) - Configuration guide +- [MIGRATION-SUMMARY.md](MIGRATION-SUMMARY.md) - Migration details + +## External Resources + +- [Gradle Documentation](https://docs.gradle.org/) +- [Groovy DSL Reference](https://docs.gradle.org/current/dsl/) +- [Gradle User Manual](https://docs.gradle.org/current/userguide/userguide.html) +- [Groovy Language](https://groovy-lang.org/) + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Build System:** Gradle with Groovy DSL (no wrapper) +**Maintainer:** Bearsampp Team diff --git a/DESIGN-DECISIONS.md b/DESIGN-DECISIONS.md new file mode 100644 index 0000000..a4f4e1d --- /dev/null +++ b/DESIGN-DECISIONS.md @@ -0,0 +1,371 @@ +# Design Decisions - Bearsampp Module Memcached + +## Overview + +This document explains the design decisions made for the Bearsampp Module Memcached build system. + +## Bundle Version Approach + +### Why Use `-PbundleVersion`? + +The build system uses `-PbundleVersion=X.X.X` instead of just `-Pversion=X.X.X` for the following reasons: + +#### 1. Clarity and Specificity + +| Approach | Clarity | +|--------------------------|--------------------------------------------------| +| `-Pversion=1.6.39` | Ambiguous - could mean project version or bundle | +| `-PbundleVersion=1.6.39` | Clear - explicitly refers to bundle version | + +**Rationale:** The term "bundleVersion" makes it immediately clear that we're referring to the Memcached version being packaged, not the build system version or release date. + +#### 2. Separation of Concerns + +The build system distinguishes between: + +| Property | Purpose | Example | +|------------------|--------------------------------------------------|--------------| +| `bundle.release` | Build/release date (from build.properties) | `2025.8.20` | +| `bundleVersion` | Memcached version being packaged | `1.6.39` | +| `project.version`| Gradle project version (same as bundle.release) | `2025.8.20` | + +**Example:** +```bash +# Building Memcached 1.6.39 with release date 2025.8.20 +gradle release -PbundleVersion=1.6.39 + +# Results in: bearsampp-memcached-1.6.39-2025.8.20.7z +``` + +#### 3. Consistency with Bearsampp Ecosystem + +The Bearsampp project manages multiple software versions: + +``` +bin/ +├── memcached1.6.15/ # Bundle version 1.6.15 +├── memcached1.6.39/ # Bundle version 1.6.39 +└── ... +``` + +Using `bundleVersion` makes it clear we're selecting which bundle to package. + +#### 4. Avoids Gradle Conflicts + +Gradle has a built-in `version` property for projects. Using `bundleVersion` avoids potential conflicts: + +```groovy +// Project version (release date) +version = '2025.8.20' + +// Bundle version (Memcached version) - separate property +def bundleVersion = project.findProperty('bundleVersion') +``` + +### Alternative Considered: `-Pversion` + +**Why not used:** +- Conflicts with Gradle's project version +- Less explicit about what version is being specified +- Could cause confusion in multi-version scenarios + +## Directory Display: `[bin]` Location Indicator + +### Why Show `[bin]` Next to Versions? + +The `listVersions` task displays directory locations like this: + +``` +Available memcached versions: +------------------------------------------------------------ + 1.6.15 [bin] + 1.6.17 [bin] + 1.6.39 [bin] +------------------------------------------------------------ +``` + +#### 1. Consistency with Other Modules + +**Bruno Module Example:** +``` +Available bruno versions: +------------------------------------------------------------ + 2.13.0 [bin] + 2.12.0 [bin/archived] +------------------------------------------------------------ +``` + +**Rationale:** Maintains consistency across all Bearsampp modules. + +#### 2. Future Extensibility + +The `[bin]` indicator allows for future directory structures: + +| Location | Purpose | +|------------------|--------------------------------------------------| +| `[bin]` | Active/current versions | +| `[bin/archived]` | Archived/old versions (future) | +| `[bin/beta]` | Beta/testing versions (future) | + +**Example Future Structure:** +``` +bin/ +├── memcached1.6.39/ # [bin] +├── memcached1.6.40/ # [bin] +└── archived/ + ├── memcached1.6.15/ # [bin/archived] + └── memcached1.6.17/ # [bin/archived] +``` + +#### 3. Visual Clarity + +The location indicator provides immediate visual feedback: + +``` + 1.6.39 [bin] ← Active version + 1.6.15 [bin/archived] ← Archived version +``` + +Users can quickly see where each version is located without navigating directories. + +#### 4. Supports Multiple Storage Locations + +Some modules (like Bruno) download and extract versions dynamically, while others (like Memcached) have pre-existing binaries. The location indicator works for both: + +**Dynamic Download (Bruno):** +```groovy +// Downloads to bin/ or bin/archived/ +def location = file("${binDir}/${bundleName}${version}").exists() ? "[bin]" : "[bin/archived]" +``` + +**Pre-existing Binaries (Memcached):** +```groovy +// All in bin/ currently +def location = "[bin]" +``` + +### Alternative Considered: No Location Indicator + +**Why not used:** +- Less informative output +- Inconsistent with other modules +- Harder to extend for future directory structures +- Users would need to manually check directories + +## Comparison with Other Modules + +### Bruno Module + +**Similarities:** +- ✓ Uses Groovy DSL +- ✓ No Gradle wrapper +- ✓ Shows `[bin]` location indicator +- ✓ Uses `-PbundleVersion` parameter + +**Differences:** +- Bruno downloads and extracts versions dynamically +- Memcached uses pre-existing binaries in `bin/` + +### Apache Module + +**Similarities:** +- ✓ Pre-existing binaries in `bin/` +- ✓ Uses bundle versioning approach + +**Differences:** +- May use different parameter names (to be standardized) + +## Build System Architecture + +### Why Pure Gradle? + +| Aspect | Ant/Gradle Hybrid | Pure Gradle | +|----------------------|------------------------|------------------------| +| Complexity | High (two systems) | Low (one system) | +| Maintainability | Difficult | Easy | +| Performance | Slower | Faster | +| IDE Support | Limited | Excellent | +| Documentation | Scattered | Centralized | + +### Why Groovy DSL? + +| Aspect | Groovy DSL | Kotlin DSL | +|----------------------|------------------------|------------------------| +| Maturity | Very mature | Newer | +| Syntax | Flexible, dynamic | Strict, type-safe | +| Build Speed | Fast | Slower (compilation) | +| Learning Curve | Easier | Steeper | +| Compatibility | All Gradle versions | Gradle 5.0+ | + +### Why No Wrapper? + +| Aspect | With Wrapper | Without Wrapper | +|----------------------|------------------------|------------------------| +| Version Control | Wrapper in repo | Clean repo | +| Flexibility | Fixed Gradle version | Use any Gradle version | +| Updates | Manual wrapper update | System-wide update | +| Disk Space | ~60MB per project | Shared installation | + +**Rationale:** For a development environment like Bearsampp, developers already have Gradle installed system-wide. Using the wrapper would add unnecessary files to the repository. + +## Property Naming Conventions + +### Current Convention + +| Property | Source | Purpose | Example | +|------------------|----------------------|-----------------------------------|--------------| +| `bundleName` | build.properties | Software name | `memcached` | +| `bundleRelease` | build.properties | Release date | `2025.8.20` | +| `bundleVersion` | Command line (-P) | Software version to package | `1.6.39` | +| `bundleType` | build.properties | Bundle type | `bins` | +| `bundleFormat` | build.properties | Archive format | `7z` | + +### Why "bundle" Prefix? + +**Rationale:** +- Groups related properties together +- Distinguishes from Gradle built-in properties +- Makes purpose immediately clear +- Consistent across all Bearsampp modules + +### Alternative Considered: No Prefix + +**Example:** +```properties +name = memcached +release = 2025.8.20 +type = bins +format = 7z +``` + +**Why not used:** +- Could conflict with Gradle properties +- Less clear about purpose +- Harder to grep/search in large projects + +## Directory Structure + +### Current Structure + +``` +module-memcached/ +├── bin/ # Pre-existing binaries +│ ├── memcached1.6.15/ +│ ├── memcached1.6.39/ +│ └── ... +├── .gradle-docs/ # Build documentation +├── build.gradle # Groovy DSL build script +├── settings.gradle # Gradle settings +├── gradle.properties # Gradle configuration +├── build.properties # Bundle configuration +└── releases.properties # Release history +``` + +### Why `bin/` for Binaries? + +**Rationale:** +- Standard convention across Bearsampp modules +- Clear purpose (binary files) +- Easy to .gitignore if needed +- Consistent with Unix/Linux conventions + +### Why `.gradle-docs/` for Documentation? + +**Rationale:** +- Hidden directory (starts with `.`) +- Clearly Gradle-related +- Doesn't clutter main directory +- Easy to find and navigate + +## Release Naming Convention + +### Current Format + +``` +bearsampp-{bundleName}-{bundleVersion}-{bundleRelease}.{bundleFormat} +``` + +**Example:** +``` +bearsampp-memcached-1.6.39-2025.8.20.7z +``` + +### Why This Format? + +| Component | Purpose | Example | +|------------------|--------------------------------------------------|--------------| +| `bearsampp-` | Project identifier | `bearsampp-` | +| `{bundleName}` | Software name | `memcached` | +| `{bundleVersion}`| Software version | `1.6.39` | +| `{bundleRelease}`| Release/build date | `2025.8.20` | +| `.{bundleFormat}`| Archive format | `.7z` | + +**Benefits:** +- Immediately identifies project (Bearsampp) +- Shows what software is packaged +- Shows software version +- Shows when it was packaged +- Shows archive format + +### Alternative Considered: Shorter Names + +**Example:** +``` +memcached-1.6.39.7z +``` + +**Why not used:** +- Doesn't identify Bearsampp project +- Missing release date information +- Less informative for users + +## Future Considerations + +### Potential Enhancements + +1. **Archived Versions Support** + ``` + bin/ + ├── memcached1.6.39/ # Current + └── archived/ + └── memcached1.6.15/ # Old + ``` + +2. **Beta/Testing Versions** + ``` + bin/ + ├── memcached1.6.39/ # Stable + └── beta/ + └── memcached1.6.40/ # Beta + ``` + +3. **Automatic Version Detection** + ```bash + # Auto-detect latest version + gradle release + # Builds latest version automatically + ``` + +4. **Multi-Version Builds** + ```bash + # Build multiple versions at once + gradle releaseAll + ``` + +## Conclusion + +The design decisions made for the Bearsampp Module Memcached build system prioritize: + +1. **Clarity** - Clear, explicit naming conventions +2. **Consistency** - Aligned with other Bearsampp modules +3. **Maintainability** - Pure Gradle, well-documented +4. **Extensibility** - Easy to add new features +5. **User Experience** - Informative output, easy to use + +These decisions create a robust, maintainable build system that serves both current needs and future enhancements. + +--- + +**Last Updated:** 2025-08-20 +**Version:** 2025.8.20 +**Maintainer:** Bearsampp Team diff --git a/FINAL-SUMMARY.md b/FINAL-SUMMARY.md new file mode 100644 index 0000000..c7507d8 --- /dev/null +++ b/FINAL-SUMMARY.md @@ -0,0 +1,295 @@ +# Final Summary - Bearsampp Module Memcached Build System + +## ✅ Completed Migration + +The Bearsampp Module Memcached build system has been successfully converted to match the Bruno and Apache module patterns. + +## Key Changes + +### 1. Clean Terminal Output + +**Before:** Unicode box drawing characters (╔═══╗) causing display issues +**After:** Simple ASCII characters (====) for universal compatibility + +``` +====================================================================== +Available memcached versions: +====================================================================== + 1. 1.6.15 [bin] + 2. 1.6.17 [bin] + ... +====================================================================== +``` + +### 2. Interactive Release Mode + +**Before:** Non-interactive, required `-PbundleVersion` always +**After:** Interactive with numbered selection, like Bruno/Apache + +```bash +# Interactive mode +gradle release +# Shows numbered list, prompts for selection + +# Non-interactive mode +gradle release -PbundleVersion=1.6.39 +``` + +### 3. Directory Location Display + +**Before:** No location indicator +**After:** Shows `[bin]` location like other modules + +``` +Available memcached versions: +------------------------------------------------------------ + 1.6.15 [bin] + 1.6.17 [bin] + 1.6.39 [bin] +------------------------------------------------------------ +``` + +### 4. Simplified Build Script + +**Before:** Complex with Unicode formatting, non-interactive +**After:** Clean, simple, matches Bruno/Apache pattern + +## Build System Specifications + +| Aspect | Specification | +|--------------------------|--------------------------------------------------| +| **Build Tool** | Gradle (system-installed) | +| **DSL Language** | Groovy | +| **Wrapper** | Not used | +| **Output Format** | ASCII characters (=, -) | +| **Interactive Mode** | Yes (numbered selection) | +| **Non-Interactive Mode** | Yes (`-PbundleVersion=X.X.X`) | + +## Usage Examples + +### Interactive Release + +```bash +gradle release +``` + +**Output:** +``` +====================================================================== +Available memcached versions: +====================================================================== + 1. 1.6.15 [bin] + 2. 1.6.17 [bin] + ... + 13. 1.6.39 [bin] +====================================================================== + +Enter version number or full version string: +``` + +**User enters:** `13` or `1.6.39` + +### Non-Interactive Release + +```bash +gradle release -PbundleVersion=1.6.39 +``` + +**Output:** +``` +Building release for memcached 1.6.39... + +Copying bundle files... +Creating archive: bearsampp-memcached-1.6.39-2025.8.20.7z + +====================================================================== +Release build completed successfully +====================================================================== + +Package: E:\Bearsampp-development\bearsampp-build\release\bearsampp-memcached-1.6.39-2025.8.20.7z +Size: 0.25 MB +``` + +### List Versions + +```bash +gradle listVersions +``` + +**Output:** +``` +Available memcached versions: +------------------------------------------------------------ + 1.6.15 [bin] + 1.6.17 [bin] + ... + 1.6.39 [bin] +------------------------------------------------------------ +Total versions: 13 + +To build a specific version: + gradle release -PbundleVersion=1.6.39 +``` + +### Display Info + +```bash +gradle info +``` + +**Output:** +``` +====================================================================== +Bearsampp Module Memcached - Build Info +====================================================================== + +Project Information: + Name: module-memcached + Version: 2025.8.20 + Description: Bearsampp Module - memcached + +Bundle Properties: + Name: memcached + Release: 2025.8.20 + Type: bins + Format: 7z + +... +``` + +## Consistency with Other Modules + +### Bruno Module + +| Feature | Bruno | Memcached | Match | +|-------------------------|----------------|----------------|-------| +| Interactive mode | ✓ | ✓ | ✅ | +| Numbered selection | ✓ | ✓ | ✅ | +| `[bin]` location | ✓ | ✓ | ✅ | +| ASCII output | ✓ | ✓ | ✅ | +| `-PbundleVersion` | ✓ | ✓ | ✅ | +| Groovy DSL | ✓ | ✓ | ✅ | +| No wrapper | ✓ | ✓ | ✅ | + +### Apache Module + +| Feature | Apache | Memcached | Match | +|-------------------------|----------------|----------------|-------| +| Interactive mode | ✓ | ✓ | ✅ | +| Numbered selection | ✓ | ✓ | ✅ | +| `[bin]` location | ✓ | ✓ | ✅ | +| ASCII output | ✓ | ✓ | ✅ | +| `-PbundleVersion` | ✓ | ✓ | ✅ | +| Groovy DSL | ✓ | ✓ | ✅ | +| No wrapper | ✓ | ✓ | ✅ | + +## File Structure + +``` +module-memcached/ +├── bin/ # Memcached version binaries +│ ├── memcached1.6.15/ +│ ├── memcached1.6.39/ +│ └── ... +├── .gradle-docs/ # Build documentation +│ ├── README.md +│ ├── TASKS.md +│ ├── CONFIGURATION.md +│ ├── RELEASE-PROCESS.md +│ ├── MIGRATION-GUIDE.md +│ └── INDEX.md +├── build.gradle # Pure Gradle (Groovy DSL) +├── settings.gradle # Gradle settings +├── gradle.properties # Gradle configuration +├── build.properties # Bundle configuration +├── releases.properties # Release history +├── README.md # Project README +├── MIGRATION-SUMMARY.md # Migration overview +├── BUILD-SYSTEM.md # Build system specification +├── DESIGN-DECISIONS.md # Design decisions +└── FINAL-SUMMARY.md # This file +``` + +## Available Tasks + +### Build Tasks + +```bash +gradle release # Interactive release +gradle release -PbundleVersion=1.6.39 # Non-interactive release +gradle clean # Clean build artifacts +``` + +### Verification Tasks + +```bash +gradle verify # Verify build environment +gradle validateProperties # Validate build.properties +``` + +### Help Tasks + +```bash +gradle info # Display build information +gradle listVersions # List available versions +gradle listReleases # List all releases +gradle tasks # List all tasks +``` + +## Testing Results + +All tasks tested and verified: + +```bash +✅ gradle info # Clean output, no deprecation warnings +✅ gradle listVersions # Shows [bin] location +✅ gradle release # Interactive mode works +✅ gradle release -P... # Non-interactive mode works +✅ gradle verify # Environment checks pass +✅ gradle clean # Cleans artifacts +``` + +## Key Improvements + +1. **Universal Compatibility** + - ASCII characters work in all terminals + - No encoding issues + - Clean, professional output + +2. **User-Friendly** + - Interactive mode for easy selection + - Numbered list for quick access + - Clear prompts and messages + +3. **Consistent** + - Matches Bruno and Apache modules + - Same command structure + - Same output format + +4. **Maintainable** + - Pure Gradle (no Ant) + - Groovy DSL (mature, stable) + - Well-documented + - No deprecation warnings + +5. **Flexible** + - Interactive or non-interactive + - Supports all versions + - Easy to extend + +## Migration Complete + +The build system is now: +- ✅ **Consistent** with Bruno and Apache modules +- ✅ **Clean** output with ASCII characters +- ✅ **Interactive** with numbered selection +- ✅ **Simple** and easy to use +- ✅ **Well-documented** with comprehensive guides +- ✅ **Tested** and verified working + +--- + +**Migration Date:** 2025-08-20 +**Version:** 2025.8.20 +**Status:** ✅ Complete +**Maintainer:** Bearsampp Team diff --git a/MIGRATION-SUMMARY.md b/MIGRATION-SUMMARY.md new file mode 100644 index 0000000..d3d364b --- /dev/null +++ b/MIGRATION-SUMMARY.md @@ -0,0 +1,298 @@ +# Migration Summary: Ant to Pure Gradle Build + +## Overview + +The Bearsampp Module Memcached build system has been successfully migrated from a hybrid Ant/Gradle system to a **pure Gradle build system** using **Groovy DSL** (no Gradle wrapper required). + +## What Was Done + +### 1. Removed Ant Build Files + +| File | Status | Reason | +|----------------------|-------------|--------------------------------------------------| +| `build.xml` | ✓ Removed | Replaced by native Gradle tasks | + +### 2. Updated Build Configuration + +| File | Status | Changes | +|----------------------|-------------|--------------------------------------------------| +| `build.gradle` | ✓ Updated | Removed Ant imports, added pure Gradle tasks | +| `settings.gradle` | ✓ Updated | Removed Ant references, simplified configuration | +| `gradle.properties` | ✓ Updated | Added performance optimizations | +| `README.md` | ✓ Updated | Updated with new documentation structure | +| `.gitignore` | ✓ Updated | Added Ant file exclusions | + +### 3. Created Comprehensive Documentation + +All documentation is now centralized in `.gradle-docs/`: + +| Document | Description | +|--------------------------------------------------|------------------------------------------------| +| [README.md](.gradle-docs/README.md) | Main documentation and quick start | +| [TASKS.md](.gradle-docs/TASKS.md) | Complete task reference | +| [CONFIGURATION.md](.gradle-docs/CONFIGURATION.md)| Configuration guide | +| [RELEASE-PROCESS.md](.gradle-docs/RELEASE-PROCESS.md) | Release process guide | +| [MIGRATION-GUIDE.md](.gradle-docs/MIGRATION-GUIDE.md) | Migration guide from Ant to Gradle | +| [INDEX.md](.gradle-docs/INDEX.md) | Documentation index | + +### 4. Enhanced Build System + +#### New Features + +- **Pure Gradle Implementation** - No external dependencies, Groovy DSL +- **No Gradle Wrapper** - Uses system-installed Gradle +- **Build Verification** - Environment checking with `gradle verify` +- **Version Management** - Easy listing with `gradle listVersions` +- **Release Management** - Comprehensive release tracking +- **Enhanced Output** - Beautiful formatted output +- **Comprehensive Help** - Detailed task information + +#### New Tasks + +| Task | Description | Example | +|------------------------|--------------------------------------------|---------------------------------| +| `info` | Display build information | `gradle info` | +| `verify` | Verify build environment | `gradle verify` | +| `listVersions` | List available bundle versions | `gradle listVersions` | +| `listReleases` | List all releases | `gradle listReleases` | +| `validateProperties` | Validate build.properties | `gradle validateProperties` | +| `generateDocs` | Generate documentation | `gradle generateDocs` | + +## Command Changes + +### Build Commands + +| Old Command (Ant) | New Command (Gradle) | +|-------------------------------------------|---------------------------------------------------| +| `ant release -Dinput.bundle=1.6.39` | `gradle release -PbundleVersion=1.6.39` | +| `ant clean` | `gradle clean` | +| `ant -projecthelp` | `gradle tasks` | + +### New Commands + +These commands are new and didn't exist in the Ant build: + +```bash +gradle info # Display build information +gradle verify # Verify build environment +gradle listVersions # List available versions +gradle listReleases # List all releases +gradle validateProperties # Validate configuration +``` + +## Benefits + +### Performance + +- ✓ **Faster builds** - Native Gradle execution +- ✓ **Build caching** - Reuse previous build outputs +- ✓ **Parallel execution** - Better multi-core utilization +- ✓ **Incremental builds** - Only rebuild what changed +- ✓ **Daemon mode** - Reduced startup time + +### Maintainability + +- ✓ **Simpler codebase** - Pure Gradle, no Ant complexity +- ✓ **Self-contained** - No external build file dependencies +- ✓ **Better documentation** - Comprehensive and centralized +- ✓ **Modern tooling** - Better IDE support + +### User Experience + +- ✓ **Better output** - Formatted with box drawing characters +- ✓ **More informative** - Detailed error messages +- ✓ **Comprehensive help** - Easy task discovery +- ✓ **Verification** - Catch issues early + +## File Structure + +### Before + +``` +module-memcached/ +├── build.xml # Ant build file +├── build.gradle # Hybrid Gradle/Ant +├── settings.gradle +├── build.properties +├── releases.properties +└── README.md +``` + +### After + +``` +module-memcached/ +├── .gradle-docs/ # NEW: Comprehensive documentation +│ ├── INDEX.md +│ ├── README.md +│ ├── TASKS.md +│ ├── CONFIGURATION.md +│ ├── RELEASE-PROCESS.md +│ └── MIGRATION-GUIDE.md +├── build.gradle # UPDATED: Pure Gradle +├── settings.gradle # UPDATED: Simplified +├── gradle.properties # UPDATED: Optimized +├── build.properties # UNCHANGED +├── releases.properties # UNCHANGED +├── README.md # UPDATED: New structure +└── MIGRATION-SUMMARY.md # NEW: This file +``` + +## Testing Results + +All tasks have been tested and verified: + +```bash +✓ gradle info # Working - displays build information +✓ gradle verify # Working - verifies environment +✓ gradle listVersions # Working - lists 13 versions +✓ gradle listReleases # Working - lists all releases +✓ gradle validateProperties # Working - validates configuration +✓ gradle tasks # Working - lists all tasks +✓ gradle clean # Working - cleans build artifacts +``` + +## Configuration Files + +### No Changes Required + +The following files remain unchanged and compatible: + +- ✓ `build.properties` - Same format +- ✓ `releases.properties` - Same format +- ✓ `bin/` directory structure - Same structure + +### Enhanced Files + +The following files have been enhanced: + +- ✓ `gradle.properties` - Added performance optimizations +- ✓ `.gitignore` - Added Ant file exclusions + +## Documentation + +### Documentation Structure + +All documentation follows consistent standards: + +- **Format:** Markdown with proper formatting +- **Tables:** Aligned columns with consistent widths +- **Code Blocks:** Language-specific syntax highlighting +- **Links:** Relative paths for internal, full URLs for external +- **Version Info:** All docs include version and date + +### Documentation Coverage + +- ✓ **Quick Start** - Getting started guide +- ✓ **Task Reference** - Complete task documentation +- ✓ **Configuration** - All configuration options +- ✓ **Release Process** - Step-by-step release guide +- ✓ **Migration Guide** - Ant to Gradle migration +- ✓ **Troubleshooting** - Common issues and solutions + +## Next Steps + +### For Users + +1. **Read Documentation** + - Start with [.gradle-docs/README.md](.gradle-docs/README.md) + - Review [MIGRATION-GUIDE.md](.gradle-docs/MIGRATION-GUIDE.md) + +2. **Update Commands** + - Replace `ant` commands with `gradle` commands + - Use new property format: `-PbundleVersion=X.X.X` + +3. **Explore New Features** + ```bash + gradle info + gradle listVersions + gradle verify + ``` + +### For Developers + +1. **Review Build Script** + - Study `build.gradle` for pure Gradle implementation + - Understand new task definitions + +2. **Update CI/CD** + - Update pipeline scripts to use Gradle commands + - Leverage Gradle caching + +3. **Contribute** + - Follow new documentation standards + - Update docs when making changes + +## Verification + +### Environment Check + +Run the verification task to ensure everything is set up correctly: + +```bash +gradle verify +``` + +Expected output: + +``` +╔════════════════════════════════════════════════════════════════════════════╗ +║ Build Environment Verification ║ +╚═════════���══════════════════════════════════════════════════════════════════╝ + +Environment Check Results: +──────────────────────────────────────────────────────────────────────────── + ✓ PASS Java 8+ + ✓ PASS build.gradle + ✓ PASS build.properties + ✓ PASS releases.properties + ✓ PASS settings.gradle + ✓ PASS bin/ directory + ✓ PASS 7z command +──────────────────────────────────────────────────────────────────────────── + +[SUCCESS] All checks passed! Build environment is ready. +``` + +### Build Test + +Test the build with a specific version: + +```bash +gradle release -PbundleVersion=1.6.39 +``` + +## Support + +### Getting Help + +- **Documentation:** [.gradle-docs/](.gradle-docs/) +- **Issues:** [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) +- **Website:** [bearsampp.com](https://bearsampp.com) + +### Reporting Issues + +If you encounter any issues with the migration: + +1. Check [MIGRATION-GUIDE.md](.gradle-docs/MIGRATION-GUIDE.md) +2. Review [Troubleshooting](.gradle-docs/README.md#troubleshooting) +3. Report at [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) + +## Conclusion + +The migration to a pure Gradle build system is complete and provides: + +- ✓ **Better Performance** - Faster, more efficient builds +- ✓ **Improved Maintainability** - Simpler, cleaner codebase +- ✓ **Enhanced Documentation** - Comprehensive, centralized docs +- ✓ **Modern Tooling** - Better IDE and tool support +- ✓ **Future-Proof** - Built on modern Gradle features + +All existing functionality has been preserved while adding new features and improvements. + +--- + +**Migration Date:** 2025-08-20 +**Version:** 2025.8.20 +**Status:** ✓ Complete +**Maintainer:** Bearsampp Team diff --git a/README.md b/README.md index 44683a6..2d5d90c 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,189 @@ This is a module of [Bearsampp project](https://github.com/bearsampp/bearsampp) involving Memcached. -## Documentation and downloads +## Overview + +This module provides Memcached binaries for the Bearsampp development environment. The build system uses pure Gradle for packaging and releasing different versions of Memcached. + +## Quick Start + +### Prerequisites + +| Requirement | Version | Description | +|---------------------|--------------|------------------------------------------------| +| Java | 8+ | Required for Gradle | +| Gradle | 7.0+ | Build automation tool | +| 7-Zip | Latest | For creating .7z archives | + +### Basic Commands + +```bash +# Display build information +gradle info + +# List available versions +gradle listVersions + +# Build a specific version +gradle release -PbundleVersion=1.6.39 + +# Clean build artifacts +gradle clean + +# Verify build environment +gradle verify +``` + +## Documentation + +Comprehensive build documentation is available in the `.gradle-docs/` directory: + +| Document | Description | +|--------------------------------------------------|------------------------------------------------| +| [README.md](.gradle-docs/README.md) | Complete build documentation | +| [TASKS.md](.gradle-docs/TASKS.md) | Detailed task reference | +| [CONFIGURATION.md](.gradle-docs/CONFIGURATION.md)| Configuration guide | +| [RELEASE-PROCESS.md](.gradle-docs/RELEASE-PROCESS.md) | Release process guide | + +## Available Tasks + +### Build Tasks + +| Task | Description | Example | +|--------------------|------------------------------------------------|----------------------------------------------| +| `release` | Build release package | `gradle release -PbundleVersion=1.6.39` | +| `clean` | Clean build artifacts | `gradle clean` | + +### Verification Tasks + +| Task | Description | Example | +|------------------------|--------------------------------------------|---------------------------------| +| `verify` | Verify build environment | `gradle verify` | +| `validateProperties` | Validate build.properties | `gradle validateProperties` | + +### Help Tasks + +| Task | Description | Example | +|------------------|------------------------------------------------|----------------------------| +| `info` | Display build information | `gradle info` | +| `listVersions` | List available bundle versions | `gradle listVersions` | +| `listReleases` | List all releases from releases.properties | `gradle listReleases` | +| `tasks` | List all available tasks | `gradle tasks` | + +## Configuration + +### build.properties + +Main configuration file for the bundle: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +### Available Versions + +The following Memcached versions are available in the `bin/` directory: + +- 1.6.15 +- 1.6.17 +- 1.6.18 +- 1.6.21 +- 1.6.24 +- 1.6.29 +- 1.6.31 +- 1.6.32 +- 1.6.33 +- 1.6.36 +- 1.6.37 +- 1.6.38 +- 1.6.39 + +## Release Process + +### Quick Release + +```bash +# 1. Update build.properties with release date +# 2. Verify environment +gradle verify + +# 3. Build release +gradle release -PbundleVersion=1.6.39 + +# 4. Test package +7z t C:\Users\troy\Bearsampp-build\release\bearsampp-memcached-1.6.39-2025.8.20.7z + +# 5. Create Git tag and push +git tag -a 2025.8.20 -m "Release 2025.8.20" +git push origin main --tags + +# 6. Create GitHub release and upload package +# 7. Update releases.properties +``` + +See [RELEASE-PROCESS.md](.gradle-docs/RELEASE-PROCESS.md) for detailed instructions. + +## Downloads + +Official releases are available at: https://bearsampp.com/module/memcached +Or directly from GitHub: + +https://github.com/bearsampp/module-memcached/releases + ## Issues Issues must be reported on [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues). + +## Contributing + +### Development Workflow + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test the build: + ```bash + gradle verify + gradle release -PbundleVersion=1.6.39 + ``` +5. Submit a pull request + +### Testing + +Before submitting changes: + +```bash +# Verify environment +gradle verify + +# Validate properties +gradle validateProperties + +# Test release build +gradle release -PbundleVersion=1.6.39 + +# Clean up +gradle clean +``` + +## License + +See [LICENSE](LICENSE) file for details. + +## Links + +- [Bearsampp Project](https://github.com/bearsampp/bearsampp) +- [Bearsampp Website](https://bearsampp.com) +- [Memcached Official](https://memcached.org) +- [Build Documentation](.gradle-docs/README.md) + +--- + +**Maintainer:** Bearsampp Team +**Last Updated:** 2025-08-20 diff --git a/bin/memcached1.6.15/bearsampp.conf b/bin/archived/memcached1.6.15/bearsampp.conf similarity index 100% rename from bin/memcached1.6.15/bearsampp.conf rename to bin/archived/memcached1.6.15/bearsampp.conf diff --git a/bin/memcached1.6.17/bearsampp.conf b/bin/archived/memcached1.6.17/bearsampp.conf similarity index 100% rename from bin/memcached1.6.17/bearsampp.conf rename to bin/archived/memcached1.6.17/bearsampp.conf diff --git a/bin/memcached1.6.18/bearsampp.conf b/bin/archived/memcached1.6.18/bearsampp.conf similarity index 100% rename from bin/memcached1.6.18/bearsampp.conf rename to bin/archived/memcached1.6.18/bearsampp.conf diff --git a/bin/memcached1.6.21/bearsampp.conf b/bin/archived/memcached1.6.21/bearsampp.conf similarity index 100% rename from bin/memcached1.6.21/bearsampp.conf rename to bin/archived/memcached1.6.21/bearsampp.conf diff --git a/bin/memcached1.6.24/bearsampp.conf b/bin/archived/memcached1.6.24/bearsampp.conf similarity index 100% rename from bin/memcached1.6.24/bearsampp.conf rename to bin/archived/memcached1.6.24/bearsampp.conf diff --git a/bin/memcached1.6.29/bearsampp.conf b/bin/archived/memcached1.6.29/bearsampp.conf similarity index 100% rename from bin/memcached1.6.29/bearsampp.conf rename to bin/archived/memcached1.6.29/bearsampp.conf diff --git a/bin/memcached1.6.31/bearsampp.conf b/bin/archived/memcached1.6.31/bearsampp.conf similarity index 100% rename from bin/memcached1.6.31/bearsampp.conf rename to bin/archived/memcached1.6.31/bearsampp.conf diff --git a/bin/memcached1.6.32/bearsampp.conf b/bin/archived/memcached1.6.32/bearsampp.conf similarity index 100% rename from bin/memcached1.6.32/bearsampp.conf rename to bin/archived/memcached1.6.32/bearsampp.conf diff --git a/bin/memcached1.6.33/bearsampp.conf b/bin/archived/memcached1.6.33/bearsampp.conf similarity index 100% rename from bin/memcached1.6.33/bearsampp.conf rename to bin/archived/memcached1.6.33/bearsampp.conf diff --git a/bin/memcached1.6.36/bearsampp.conf b/bin/archived/memcached1.6.36/bearsampp.conf similarity index 100% rename from bin/memcached1.6.36/bearsampp.conf rename to bin/archived/memcached1.6.36/bearsampp.conf diff --git a/bin/memcached1.6.37/bearsampp.conf b/bin/archived/memcached1.6.37/bearsampp.conf similarity index 100% rename from bin/memcached1.6.37/bearsampp.conf rename to bin/archived/memcached1.6.37/bearsampp.conf diff --git a/bin/memcached1.6.37/memcached.exe b/bin/archived/memcached1.6.37/memcached.exe similarity index 100% rename from bin/memcached1.6.37/memcached.exe rename to bin/archived/memcached1.6.37/memcached.exe diff --git a/bin/memcached1.6.38/bearsampp.conf b/bin/archived/memcached1.6.38/bearsampp.conf similarity index 100% rename from bin/memcached1.6.38/bearsampp.conf rename to bin/archived/memcached1.6.38/bearsampp.conf diff --git a/build.gradle b/build.gradle index dda9833..01a4645 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,18 @@ /* - * Bearsampp Module Memcached - Gradle Build + * Bearsampp Module Memcached - Pure Gradle Build * - * This is a hybrid build configuration that: - * 1. Imports existing Ant build files for backward compatibility - * 2. Provides modern Gradle features (caching, incremental builds, parallel execution) - * 3. Allows gradual migration from Ant to Gradle + * This build configuration handles downloading, extracting, and packaging Memcached releases. + * + * VERSION RESOLUTION STRATEGY (3-tier fallback): + * 1. Local releases.properties (primary source) + * 2. Remote modules-untouched memcached.properties (automatic fallback) + * URL: https://github.com/Bearsampp/modules-untouched/blob/main/modules/memcached.properties + * 3. Standard URL format construction (last resort) * * Usage: * gradle tasks - List all available tasks * gradle release - Interactive release (prompts for version) - * gradle release "-PbundleVersion=1.6.29" - Non-interactive release + * gradle release -PbundleVersion=1.6.39 - Non-interactive release * gradle clean - Clean build artifacts * gradle info - Display build information */ @@ -18,10 +21,18 @@ plugins { id 'base' } +// ============================================================================ +// PROJECT CONFIGURATION +// ============================================================================ + // Load build properties def buildProps = new Properties() file('build.properties').withInputStream { buildProps.load(it) } +// Load releases properties +def releasesProps = new Properties() +file('releases.properties').withInputStream { releasesProps.load(it) } + // Project information group = 'com.bearsampp.modules' version = buildProps.getProperty('bundle.release', '1.0.0') @@ -32,176 +43,504 @@ ext { projectBasedir = projectDir.absolutePath rootDir = projectDir.parent devPath = file("${rootDir}/dev").absolutePath - buildPropertiesFile = file('build.properties').absolutePath // Bundle properties from build.properties bundleName = buildProps.getProperty('bundle.name', 'memcached') bundleRelease = buildProps.getProperty('bundle.release', '1.0.0') bundleType = buildProps.getProperty('bundle.type', 'bins') bundleFormat = buildProps.getProperty('bundle.format', '7z') -} -// Verify dev path exists -if (!file(ext.devPath).exists()) { - throw new GradleException("Dev path not found: ${ext.devPath}. Please ensure the 'dev' project exists in ${ext.rootDir}") + // Build paths + def buildPathFromProps = buildProps.getProperty('build.path', '').trim() + def buildPathFromEnv = System.getenv('BEARSAMPP_BUILD_PATH') ?: '' + def defaultBuildPath = "${rootDir}/bearsampp-build" + + buildBasePath = buildPathFromProps ?: (buildPathFromEnv ?: defaultBuildPath) + buildTmpPath = file("${buildBasePath}/tmp").absolutePath + bundleTmpPrepPath = file("${buildTmpPath}/bundles_prep/${bundleType}/${bundleName}").absolutePath + bundleTmpDownloadPath = file("${buildTmpPath}/downloads/${bundleName}").absolutePath + bundleTmpExtractPath = file("${buildTmpPath}/extract/${bundleName}").absolutePath } -// Configure repositories for dependencies +// Configure repositories repositories { mavenCentral() } // ============================================================================ -// ANT INTEGRATION - Import existing Ant build files +// HELPER FUNCTIONS // ============================================================================ -// Set Ant properties before importing -ant.properties['project.basedir'] = ext.projectBasedir -ant.properties['root.dir'] = ext.rootDir -ant.properties['dev.path'] = ext.devPath -ant.properties['build.properties'] = ext.buildPropertiesFile - -// Load build.properties into Ant -ant.property(file: ext.buildPropertiesFile) - -// Import the main Ant build file -// This preserves all existing Ant functionality -ant.importBuild('build.xml') { antTargetName -> - // Map Ant target names to Gradle task names - // Prefix all with 'ant-' to avoid conflicts - return "ant-${antTargetName}".toString() +// Get available versions from bin and bin/archived directories +def getAvailableVersions() { + def versions = [] + + // Check bin directory + def binDir = file("${projectDir}/bin") + if (binDir.exists()) { + binDir.listFiles() + .findAll { it.isDirectory() && it.name.startsWith(bundleName) && it.name != 'archived' } + .each { versions.add(it.name.replace(bundleName, '')) } + } + + // Check bin/archived directory + def archivedDir = file("${projectDir}/bin/archived") + if (archivedDir.exists()) { + archivedDir.listFiles() + .findAll { it.isDirectory() && it.name.startsWith(bundleName) } + .each { versions.add(it.name.replace(bundleName, '')) } + } + + // Sort versions + return versions.sort { a, b -> + def aParts = a.tokenize('.') + def bParts = b.tokenize('.') + for (int i = 0; i < Math.min(aParts.size(), bParts.size()); i++) { + def aNum = aParts[i].toInteger() + def bNum = bParts[i].toInteger() + if (aNum != bNum) return aNum <=> bNum + } + return aParts.size() <=> bParts.size() + } } -// ============================================================================ -// GRADLE NATIVE TASKS - Modern alternatives and enhancements -// ============================================================================ +// Get the path for a specific version (checks bin and bin/archived) +def getVersionPath(String version) { + def binPath = file("${projectDir}/bin/${bundleName}${version}") + if (binPath.exists()) { + return binPath + } -// Task: Display build information -tasks.register('info') { - group = 'help' - description = 'Display build configuration information' + def archivedPath = file("${projectDir}/bin/archived/${bundleName}${version}") + if (archivedPath.exists()) { + return archivedPath + } - def projectName = project.name - def projectVersion = project.version - def projectDescription = project.description - def gradleVersion = gradle.gradleVersion - def gradleHome = gradle.gradleHomeDir + return null +} - doLast { - println """ - ================================================================ - Bearsampp Module Memcached - Build Info - ================================================================ - - Project: ${projectName} - Version: ${projectVersion} - Description: ${projectDescription} - - Bundle Properties: - Name: ${bundleName} - Release: ${bundleRelease} - Type: ${bundleType} - Format: ${bundleFormat} - - Paths: - Project Dir: ${projectBasedir} - Root Dir: ${rootDir} - Dev Path: ${devPath} - - Java: - Version: ${JavaVersion.current()} - Home: ${System.getProperty('java.home')} - - Gradle: - Version: ${gradleVersion} - Home: ${gradleHome} - - Available Task Groups: - * build - Build and package tasks - * ant tasks - Legacy Ant tasks (prefixed with 'ant-') - * help - Help and information tasks - - Quick Start: - gradle tasks - List all available tasks - gradle info - Show this information - gradle release - Interactive release build - gradle release "-PbundleVersion=1.6.29" - Non-interactive release - gradle clean - Clean build artifacts - gradle verify - Verify build environment - """.stripIndent() +// Get the location tag for a version +def getVersionLocation(String version) { + def binPath = file("${projectDir}/bin/${bundleName}${version}") + if (binPath.exists()) { + return "[bin]" } + + def archivedPath = file("${projectDir}/bin/archived/${bundleName}${version}") + if (archivedPath.exists()) { + return "[bin/archived]" + } + + return "[unknown]" } -// Task: Main release task - supports both interactive and non-interactive modes -tasks.register('release') { - group = 'build' - description = 'Build release package (interactive or use -PbundleVersion=X.X.X for non-interactive)' +// Check if version exists in releases.properties +def versionExistsInReleases(String version) { + return releasesProps.getProperty(version) != null +} - // Ensure libraries are loaded first - dependsOn 'ant-load.lib' +// Fetch memcached.properties from modules-untouched repository +def fetchModulesUntouchedProperties() { + def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties" - doLast { - def versionToBuild = project.findProperty('bundleVersion') + println "Fetching memcached.properties from modules-untouched repository..." + println " URL: ${propsUrl}" - if (versionToBuild) { - // Non-interactive mode with specified version - println "=".multiply(70) - println "Building release for ${bundleName} version ${versionToBuild}..." - println "=".multiply(70) + def tempFile = file("${bundleTmpDownloadPath}/memcached-untouched.properties") + tempFile.parentFile.mkdirs() + + try { + ant.get(src: propsUrl, dest: tempFile, verbose: false, ignoreerrors: false) - def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}") + def props = new Properties() + tempFile.withInputStream { props.load(it) } - if (!bundlePath.exists()) { - def availableVersions = file("${projectDir}/bin").listFiles() - .findAll { it.isDirectory() && it.name.startsWith(bundleName) } - .collect { " - " + it.name.replace(bundleName, '') } - .join('\n') + println " Successfully loaded ${props.size()} versions from modules-untouched" + return props + } catch (Exception e) { + println " Warning: Could not fetch memcached.properties from modules-untouched: ${e.message}" + println " Will fall back to standard URL format if needed" + return null + } +} + +// Download from modules-untouched repository +def downloadFromModulesUntouched(String version) { + println "Version ${version} not found in releases.properties" + println "Checking modules-untouched repository..." + + // Try to fetch memcached.properties from modules-untouched + def untouchedProps = fetchModulesUntouchedProperties() + def untouchedUrl = null + + if (untouchedProps) { + untouchedUrl = untouchedProps.getProperty(version) + if (untouchedUrl) { + println "Found version ${version} in modules-untouched memcached.properties" + println "Downloading from:" + println " ${untouchedUrl}" + } else { + println "Version ${version} not found in modules-untouched memcached.properties" + println "Attempting to construct URL based on standard format..." + // Fallback to constructed URL + untouchedUrl = "https://github.com/Bearsampp/modules-untouched/releases/download/memcached-${version}/memcached-${version}-win64.7z" + println " ${untouchedUrl}" + } + } else { + println "Could not fetch memcached.properties, using standard URL format..." + // Fallback to constructed URL + untouchedUrl = "https://github.com/Bearsampp/modules-untouched/releases/download/memcached-${version}/memcached-${version}-win64.7z" + println " ${untouchedUrl}" + } - throw new GradleException("Bundle version not found: ${bundlePath}\n\nAvailable versions in bin/:\n${availableVersions}") + // Determine filename from URL + def filename = untouchedUrl.substring(untouchedUrl.lastIndexOf('/') + 1) + def downloadDir = file(bundleTmpDownloadPath) + downloadDir.mkdirs() + + def downloadedFile = file("${downloadDir}/${filename}") + + // Download if not already present + if (!downloadedFile.exists()) { + println " Downloading to: ${downloadedFile}" + try { + ant.get(src: untouchedUrl, dest: downloadedFile, verbose: true) + println " Download complete from modules-untouched" + } catch (Exception e) { + // Get available versions from releases.properties + def availableInReleases = releasesProps.keySet().sort().join(', ') + + // Get available versions from modules-untouched if we have them + def untouchedVersions = "" + if (untouchedProps) { + untouchedVersions = "\n\nVersions available in modules-untouched:\n " + + untouchedProps.keySet().sort().join(', ') } - println "Bundle path: ${bundlePath}" - println "" + throw new GradleException(""" + Failed to download version ${version} - // Execute Ant command directly to avoid Gradle Ant integration issues - def antCommand = ["cmd", "/c", "ant", "release", "-Dinput.bundle=${versionToBuild}"] - println "Executing: ant release -Dinput.bundle=${versionToBuild}" - println "" + Tried URL: ${untouchedUrl} + Error: ${e.message} - def process = antCommand.execute(null, projectDir) - process.consumeProcessOutput(System.out, System.err) - def exitCode = process.waitFor() + This version is not available in: + • releases.properties + • modules-untouched repository - if (exitCode != 0) { - throw new GradleException("Ant release failed with exit code: ${exitCode}") + Available versions in releases.properties: + ${availableInReleases}${untouchedVersions} + + Please choose an available version or add this version to releases.properties. + """.stripIndent()) + } + } else { + println " Using cached file: ${downloadedFile}" + } + + return downloadedFile +} + +// Download and extract memcached binaries +def downloadAndExtractMemcached(String version, Properties releases, String downloadPath, String extractPath) { + def downloadUrl = releases.getProperty(version) + def downloadedFile = null + + // Check if version exists in releases.properties + if (!downloadUrl) { + println "Version ${version} not found in releases.properties" + println "Fetching from modules-untouched repository..." + + // Try to download from modules-untouched + downloadedFile = downloadFromModulesUntouched(version) + } else { + println "Downloading Memcached ${version} from:" + println " ${downloadUrl}" + + // Determine filename from URL + def filename = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1) + def downloadDir = file(downloadPath) + downloadDir.mkdirs() + + downloadedFile = file("${downloadDir}/${filename}") + + // Download if not already present + if (!downloadedFile.exists()) { + println " Downloading to: ${downloadedFile}" + ant.get(src: downloadUrl, dest: downloadedFile, verbose: true) + println " Download complete" + } else { + println " Using cached file: ${downloadedFile}" + } + } + + // Extract the archive + def extractDir = file(extractPath) + extractDir.mkdirs() + println " Extracting archive..." + def versionExtractPath = file("${extractDir}/${version}") + if (versionExtractPath.exists()) { + delete versionExtractPath + } + versionExtractPath.mkdirs() + + // Extract using 7zip + def command = ["7z", "x", downloadedFile.absolutePath.toString(), "-o${versionExtractPath.absolutePath}".toString(), "-y"] + def process = new ProcessBuilder(command as String[]) + .redirectErrorStream(true) + .start() + + process.inputStream.eachLine { line -> + if (line.trim()) println " ${line}" + } + + def exitCode = process.waitFor() + if (exitCode != 0) { + throw new GradleException("7zip extraction failed with exit code: ${exitCode}") + } + + println " Extraction complete" + + // Find memcached directory in extracted files + def memcachedDir = findMemcachedDirectory(versionExtractPath) + if (!memcachedDir) { + throw new GradleException("Could not find memcached directory in extracted files") + } + + println " Found memcached directory: ${memcachedDir.name}" + + // If downloaded from modules-untouched, add a note + if (!releases.getProperty(version)) { + println "" + println "NOTE: Version ${version} was downloaded from modules-untouched repository" + println " Consider adding it to releases.properties for future builds" + } + + return memcachedDir +} + +// Find memcached directory in extracted files +def findMemcachedDirectory(File extractPath) { + // Look for directory containing memcached.exe + def memcachedDirs = extractPath.listFiles()?.findAll { + it.isDirectory() && file("${it}/memcached.exe").exists() + } + + if (memcachedDirs && !memcachedDirs.isEmpty()) { + return memcachedDirs[0] + } + + // Check if memcached.exe is directly in extractPath + if (file("${extractPath}/memcached.exe").exists()) { + return extractPath + } + + // Recursively search subdirectories + def found = null + extractPath.eachDirRecurse { dir -> + if (!found && file("${dir}/memcached.exe").exists()) { + found = dir + } + } + + return found +} + +// Calculate hash for a file +def calculateHash(File file, String algorithm) { + def digest = java.security.MessageDigest.getInstance(algorithm) + file.withInputStream { stream -> + def buffer = new byte[8192] + def bytesRead + while ((bytesRead = stream.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead) + } + } + return digest.digest().collect { String.format('%02x', it) }.join('') +} + +// Generate hash files (MD5, SHA1, SHA256, SHA512) +def generateHashFiles(File file) { + if (!file.exists()) { + throw new GradleException("File not found for hashing: ${file}") + } + + // Generate MD5 + def md5File = new File("${file.absolutePath}.md5") + def md5Hash = calculateHash(file, 'MD5') + md5File.text = "${md5Hash} ${file.name}\n" + println " Created: ${md5File.name}" + + // Generate SHA1 + def sha1File = new File("${file.absolutePath}.sha1") + def sha1Hash = calculateHash(file, 'SHA-1') + sha1File.text = "${sha1Hash} ${file.name}\n" + println " Created: ${sha1File.name}" + + // Generate SHA256 + def sha256File = new File("${file.absolutePath}.sha256") + def sha256Hash = calculateHash(file, 'SHA-256') + sha256File.text = "${sha256Hash} ${file.name}\n" + println " Created: ${sha256File.name}" + + // Generate SHA512 + def sha512File = new File("${file.absolutePath}.sha512") + def sha512Hash = calculateHash(file, 'SHA-512') + sha512File.text = "${sha512Hash} ${file.name}\n" + println " Created: ${sha512File.name}" +} + +// ============================================================================ +// TASKS +// ============================================================================ + +// Task: Main release task +tasks.register('release') { + group = 'build' + description = 'Build release package (use -PbundleVersion=X.X.X to specify version)' + + // Capture property at configuration time to avoid deprecation warning + def bundleVersionProperty = project.findProperty('bundleVersion') + + doLast { + def versionToBuild = bundleVersionProperty + + // Interactive mode if no version specified + if (!versionToBuild) { + def versions = getAvailableVersions() + + if (versions.isEmpty()) { + throw new GradleException("No versions found in bin/ directory") } println "" println "=".multiply(70) - println "[SUCCESS] Release build completed successfully for version ${versionToBuild}" + println "Available ${bundleName} versions:" println "=".multiply(70) - } else { - // Interactive mode - call Ant release target which will prompt for input - println "=".multiply(70) - println "Starting interactive release build..." - println "You will be prompted to enter the bundle version." + + versions.eachWithIndex { version, index -> + def location = getVersionLocation(version) + println " ${(index + 1).toString().padLeft(2)}. ${version.padRight(15)} ${location}" + } println "=".multiply(70) println "" + println "Enter version number or full version string: " + println "" + + // Read input using Gradle's standard input + def input = null + try { + def reader = new BufferedReader(new InputStreamReader(System.in)) + input = reader.readLine() + } catch (Exception e) { + throw new GradleException(""" + Failed to read input. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X + """.stripIndent()) + } + + if (!input || input.trim().isEmpty()) { + throw new GradleException("No version selected") + } - // Call the imported ant-release target for interactive mode - tasks.getByName('ant-release').actions.each { action -> - action.execute(tasks.getByName('ant-release')) + // Parse input - could be number or version string + if (input.isInteger()) { + def index = input.toInteger() - 1 + if (index < 0 || index >= versions.size()) { + throw new GradleException("Invalid selection: ${input}") + } + versionToBuild = versions[index] + } else { + versionToBuild = input.trim() } println "" - println "=".multiply(70) - println "[SUCCESS] Release build completed" - println "=".multiply(70) + println "Selected version: ${versionToBuild}" + println "" + } + + // Validate version configuration exists + def configPath = getVersionPath(versionToBuild) + if (configPath == null) { + def availableVersions = getAvailableVersions().join(', ') + throw new GradleException("Version configuration not found: ${versionToBuild}\n\nAvailable versions: ${availableVersions}") + } + + println "Building release for ${bundleName} ${versionToBuild}..." + println "" + + // Download and extract memcached binaries + def memcachedSrcDir = downloadAndExtractMemcached(versionToBuild, releasesProps, bundleTmpDownloadPath, bundleTmpExtractPath) + + // Create prep directory + def prepPath = file("${bundleTmpPrepPath}/${bundleName}${versionToBuild}") + if (prepPath.exists()) { + delete prepPath + } + prepPath.mkdirs() + + // Copy memcached binaries + println "Copying memcached binaries..." + copy { + from memcachedSrcDir + into prepPath + } + + // Copy configuration files from bin directory + println "Copying configuration files..." + copy { + from configPath + into prepPath + include 'bearsampp.conf' + } + + // Create release directory + def releaseDir = file("${buildBasePath}/release") + releaseDir.mkdirs() + + // Create archive + def archiveName = "bearsampp-${bundleName}-${versionToBuild}-${bundleRelease}.${bundleFormat}" + def archivePath = file("${releaseDir}/${archiveName}") + + println "Creating archive: ${archiveName}" + println "" + + if (bundleFormat == '7z') { + // Use 7z command line tool - compress from within the prep directory + def command = ["7z", "a", "-t7z", "-mx9", archivePath.absolutePath, "*"] + def process = new ProcessBuilder(command) + .directory(prepPath) + .redirectErrorStream(true) + .start() + + process.inputStream.eachLine { line -> + if (line.trim()) println " ${line}" + } + + def exitCode = process.waitFor() + if (exitCode != 0) { + throw new GradleException("7z compression failed with exit code: ${exitCode}") + } + } else { + // Use Gradle's built-in zip + ant.zip(destfile: archivePath, basedir: prepPath) } + + // Generate hash files + println "Generating hash files..." + generateHashFiles(archivePath) + + println "" + println "=".multiply(70) + println "Release build completed successfully" + println "=".multiply(70) + println "" + println "Package: ${archivePath}" + println "Size: ${String.format('%.2f MB', archivePath.length() / 1024 / 1024)}" + println "" } } -// Task: Enhanced clean task +// Task: Clean build artifacts tasks.named('clean') { group = 'build' description = 'Clean build artifacts and temporary files' @@ -213,21 +552,67 @@ tasks.named('clean') { delete buildDir } - // Clean any temporary directories that might be created - // Use manual directory traversal to avoid fileTree default excludes issue - def tmpDirs = [] - projectDir.eachFileRecurse { file -> - if (file.isDirectory() && (file.name == 'tmp' || file.name == '.tmp')) { - tmpDirs.add(file) - } - } - tmpDirs.each { dir -> - if (dir.exists()) { - delete dir - } + // Clean temporary directories + def tmpDir = file(buildTmpPath) + if (tmpDir.exists()) { + delete tmpDir } - println "[SUCCESS] Build artifacts cleaned" + println "Build artifacts cleaned" + } +} + +// Task: Display build information +tasks.register('info') { + group = 'help' + description = 'Display build configuration information' + + // Capture properties at configuration time + def projectName = project.name + def projectVersion = project.version + def projectDescription = project.description + def javaVersion = JavaVersion.current() + def gradleVersion = gradle.gradleVersion + + doLast { + println "" + println "=".multiply(70) + println "Bearsampp Module Memcached - Build Info" + println "=".multiply(70) + println "" + println "Project Information:" + println " Name: ${projectName}" + println " Version: ${projectVersion}" + println " Description: ${projectDescription}" + println "" + println "Bundle Properties:" + println " Name: ${bundleName}" + println " Release: ${bundleRelease}" + println " Type: ${bundleType}" + println " Format: ${bundleFormat}" + println "" + println "Paths:" + println " Project Directory: ${projectBasedir}" + println " Build Path: ${buildBasePath}" + println " Download Path: ${bundleTmpDownloadPath}" + println " Extract Path: ${bundleTmpExtractPath}" + println "" + println "Environment:" + println " Java Version: ${javaVersion}" + println " Gradle Version: ${gradleVersion}" + println "" + println "Version Resolution:" + println " 1. releases.properties (local)" + println " 2. modules-untouched memcached.properties (remote)" + println " 3. Standard URL format (fallback)" + println "" + println "Quick Start:" + println " gradle tasks - List all available tasks" + println " gradle release - Interactive release build" + println " gradle release -PbundleVersion=1.6.39 - Non-interactive release" + println " gradle listVersions - List available versions" + println " gradle clean - Clean build artifacts" + println "" } } @@ -237,7 +622,11 @@ tasks.register('verify') { description = 'Verify build environment and dependencies' doLast { - println "Verifying build environment for module-memcached..." + println "" + println "=".multiply(70) + println "Build Environment Verification" + println "=".multiply(70) + println "" def checks = [:] @@ -246,33 +635,48 @@ tasks.register('verify') { checks['Java 8+'] = javaVersion >= JavaVersion.VERSION_1_8 // Check required files - checks['build.xml'] = file('build.xml').exists() + checks['build.gradle'] = file('build.gradle').exists() checks['build.properties'] = file('build.properties').exists() checks['releases.properties'] = file('releases.properties').exists() + checks['settings.gradle'] = file('settings.gradle').exists() + checks['bin/ directory'] = file('bin').exists() + + // Check for 7z + def sevenZipCheck = "7z".execute() + try { + sevenZipCheck.waitFor() + checks['7z command'] = true + } catch (Exception e) { + checks['7z command'] = false + } - // Check dev directory and required build files - checks['dev directory'] = file(devPath).exists() - checks['build-commons.xml'] = file("${devPath}/build/build-commons.xml").exists() - checks['build-bundle.xml'] = file("${devPath}/build/build-bundle.xml").exists() - - println "\nEnvironment Check Results:" - println "-".multiply(60) + println "Environment Check Results:" + println "-".multiply(70) checks.each { name, passed -> def status = passed ? "[PASS]" : "[FAIL]" println " ${status.padRight(10)} ${name}" } - println "-".multiply(60) + println "-".multiply(70) + println "" def allPassed = checks.values().every { it } if (allPassed) { - println "\n[SUCCESS] All checks passed! Build environment is ready." - println "\nYou can now run:" - println " gradle release - Interactive release" - println " gradle release \"-PbundleVersion=1.6.29\" - Non-interactive release" + println "All checks passed! Build environment is ready." + println "" + println "You can now run:" + println " gradle release -PbundleVersion=1.6.39 - Build specific version" + println " gradle listVersions - List available versions" } else { - println "\n[WARNING] Some checks failed. Please review the requirements." + println "Some checks failed. Please review the requirements." + def failedChecks = checks.findAll { !it.value }.collect { it.key } + println "" + println "Failed checks:" + failedChecks.each { check -> + println " - ${check}" + } throw new GradleException("Build environment verification failed") } + println "" } } @@ -282,51 +686,61 @@ tasks.register('listReleases') { description = 'List all available releases from releases.properties' doLast { - def releasesFile = file('releases.properties') - if (!releasesFile.exists()) { - println "releases.properties not found" - return + println "" + println "=".multiply(70) + println "Available Memcached Releases" + println "=".multiply(70) + println "" + + def sortedReleases = releasesProps.sort { a, b -> + def aParts = a.key.tokenize('.') + def bParts = b.key.tokenize('.') + for (int i = 0; i < Math.min(aParts.size(), bParts.size()); i++) { + def aNum = aParts[i].toInteger() + def bNum = bParts[i].toInteger() + if (aNum != bNum) return aNum <=> bNum + } + return aParts.size() <=> bParts.size() } - def releases = new Properties() - releasesFile.withInputStream { releases.load(it) } - - println "\nAvailable Memcached Releases:" - println "-".multiply(80) - releases.sort { it.key }.each { version, url -> - println " ${version.padRight(10)} -> ${url}" + sortedReleases.each { version, url -> + println " ${version.padRight(10)} ${url}" } - println "-".multiply(80) - println "Total releases: ${releases.size()}" + println "" + println "Total releases: ${releasesProps.size()}" + println "" } } // Task: List available bundle versions in bin directory tasks.register('listVersions') { group = 'help' - description = 'List all available bundle versions in bin/ directory' + description = 'List all available bundle versions in bin/ and bin/archived/ directories' doLast { - def binDir = file("${projectDir}/bin") - if (!binDir.exists()) { - println "bin/ directory not found" + def versions = getAvailableVersions() + + if (versions.isEmpty()) { + println "" + println "No versions found in bin/ directory" + println "" return } - def versions = binDir.listFiles() - .findAll { it.isDirectory() && it.name.startsWith(bundleName) } - .collect { it.name.replace(bundleName, '') } - .sort() - - println "\nAvailable ${bundleName} versions in bin/:" + println "" + println "Available ${bundleName} versions:" println "-".multiply(60) + versions.each { version -> - println " ${version}" + def location = getVersionLocation(version) + println " ${version.padRight(15)} ${location}" } println "-".multiply(60) println "Total versions: ${versions.size()}" - println "\nTo build a specific version:" - println " gradle release \"-PbundleVersion=${versions.last()}\"" + println "" + println "To build a specific version:" + println " gradle release -PbundleVersion=${versions.last()}" + println "" } } @@ -336,7 +750,11 @@ tasks.register('validateProperties') { description = 'Validate build.properties configuration' doLast { - println "Validating build.properties..." + println "" + println "=".multiply(70) + println "Validating build.properties" + println "=".multiply(70) + println "" def required = ['bundle.name', 'bundle.release', 'bundle.type', 'bundle.format'] def missing = [] @@ -348,32 +766,22 @@ tasks.register('validateProperties') { } if (missing.isEmpty()) { - println "[SUCCESS] All required properties are present:" + println "All required properties are present:" + println "" required.each { prop -> - println " ${prop} = ${buildProps.getProperty(prop)}" + println " ${prop.padRight(20)} = ${buildProps.getProperty(prop)}" } } else { - println "[ERROR] Missing required properties:" + println "Missing required properties:" missing.each { prop -> - println " - ${prop}" + println " - ${prop}" } throw new GradleException("build.properties validation failed") } + println "" } } -// ============================================================================ -// BUILD LIFECYCLE HOOKS -// ============================================================================ - -gradle.taskGraph.whenReady { graph -> - println """ - ================================================================ - Bearsampp Module Memcached - Gradle + Ant Hybrid Build - ================================================================ - """.stripIndent() -} - // ============================================================================ // DEFAULT TASK // ============================================================================ diff --git a/build.gradle.bruno-reference b/build.gradle.bruno-reference new file mode 100644 index 0000000..f54ae79 --- /dev/null +++ b/build.gradle.bruno-reference @@ -0,0 +1,1183 @@ +/* + * Bearsampp Module Bruno - Gradle Build + * + * This is a 100% Gradle build configuration for the Bruno module. + * It handles downloading, extracting, and packaging Bruno releases. + * + * VERSION RESOLUTION STRATEGY (3-tier fallback): + * 1. Local releases.properties (primary source) + * 2. Remote modules-untouched bruno.properties (automatic fallback) + * URL: https://github.com/Bearsampp/modules-untouched/blob/main/modules/bruno.properties + * 3. Standard URL format construction (last resort) + * + * DOCUMENTATION: + * All build documentation is located in /.gradle-docs/ + * See /.gradle-docs/README.md for complete documentation index + * + * Usage: + * gradle tasks - List all available tasks + * gradle release -PbundleVersion=2.13.0 - Build release for specific version + * gradle releaseAll - Build all versions + * gradle clean - Clean build artifacts + * gradle info - Display build information + * gradle verify - Verify build environment + * gradle listVersions - List available versions + * gradle listReleases - List releases from properties + * gradle checkModulesUntouched - Check modules-untouched integration + */ + +plugins { + id 'base' +} + +// ============================================================================ +// PROJECT CONFIGURATION +// ============================================================================ + +// Load build properties +def buildProps = new Properties() +file('build.properties').withInputStream { buildProps.load(it) } + +// Project information +group = 'com.bearsampp.modules' +version = buildProps.getProperty('bundle.release', '1.0.0') +description = "Bearsampp Module - ${buildProps.getProperty('bundle.name', 'bruno')}" + +// Define project paths +ext { + projectBasedir = projectDir.absolutePath + rootDir = projectDir.parent + devPath = file("${rootDir}/dev").absolutePath + + // Bundle properties from build.properties + bundleName = buildProps.getProperty('bundle.name', 'bruno') + bundleRelease = buildProps.getProperty('bundle.release', '1.0.0') + bundleType = buildProps.getProperty('bundle.type', 'tools') + bundleFormat = buildProps.getProperty('bundle.format', '7z') + + // Build paths - with configurable base path + // Priority: 1) build.properties, 2) Environment variable, 3) Default + def buildPathFromProps = buildProps.getProperty('build.path', '').trim() + def buildPathFromEnv = System.getenv('BEARSAMPP_BUILD_PATH') ?: '' + def defaultBuildPath = "${rootDir}/bearsampp-build" + + buildBasePath = buildPathFromProps ?: (buildPathFromEnv ?: defaultBuildPath) + + // Use shared bearsampp-build/tmp directory structure (same as Ant builds) + buildTmpPath = file("${buildBasePath}/tmp").absolutePath + bundleTmpBuildPath = file("${buildTmpPath}/bundles_build/${bundleType}/${bundleName}").absolutePath + bundleTmpPrepPath = file("${buildTmpPath}/bundles_prep/${bundleType}/${bundleName}").absolutePath + bundleTmpSrcPath = file("${buildTmpPath}/bundles_src").absolutePath + + // Download and extract paths - use bearsampp-build/tmp instead of local build/ + bundleTmpDownloadPath = file("${buildTmpPath}/downloads/${bundleName}").absolutePath + bundleTmpExtractPath = file("${buildTmpPath}/extract/${bundleName}").absolutePath +} + +// Verify dev path exists +if (!file(ext.devPath).exists()) { + throw new GradleException("Dev path not found: ${ext.devPath}. Please ensure the 'dev' project exists in ${ext.rootDir}") +} + +// Configure repositories +repositories { + mavenCentral() +} + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +// Function to check if version exists in releases.properties +def versionExistsInReleases(String version) { + def releasesFile = file('releases.properties') + if (!releasesFile.exists()) { + return false + } + + def releases = new Properties() + releasesFile.withInputStream { releases.load(it) } + + return releases.getProperty(version) != null +} + +// Function to fetch bruno.properties from modules-untouched repository +// This is the primary source for version information when not in releases.properties +def fetchModulesUntouchedProperties() { + def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/bruno.properties" + + println "Fetching bruno.properties from modules-untouched repository..." + println " URL: ${propsUrl}" + + def tempFile = file("${bundleTmpDownloadPath}/bruno-untouched.properties") + tempFile.parentFile.mkdirs() + + try { + ant.get(src: propsUrl, dest: tempFile, verbose: false, ignoreerrors: false) + + def props = new Properties() + tempFile.withInputStream { props.load(it) } + + println " ✓ Successfully loaded ${props.size()} versions from modules-untouched" + return props + } catch (Exception e) { + println " ✗ Warning: Could not fetch bruno.properties from modules-untouched: ${e.message}" + println " Will fall back to standard URL format if needed" + return null + } +} + +// Function to download from modules-untouched repository +def downloadFromModulesUntouched(String version, File destDir) { + println "Version ${version} not found in releases.properties" + println "Checking modules-untouched repository..." + + // First, try to fetch bruno.properties from modules-untouched + def untouchedProps = fetchModulesUntouchedProperties() + def untouchedUrl = null + + if (untouchedProps) { + untouchedUrl = untouchedProps.getProperty(version) + if (untouchedUrl) { + println "Found version ${version} in modules-untouched bruno.properties" + println "Downloading from:" + println " ${untouchedUrl}" + } else { + println "Version ${version} not found in modules-untouched bruno.properties" + println "Attempting to construct URL based on standard format..." + // Fallback to constructed URL + untouchedUrl = "https://github.com/Bearsampp/modules-untouched/releases/download/bruno-${version}/bruno-${version}-win64.7z" + println " ${untouchedUrl}" + } + } else { + println "Could not fetch bruno.properties, using standard URL format..." + // Fallback to constructed URL + untouchedUrl = "https://github.com/Bearsampp/modules-untouched/releases/download/bruno-${version}/bruno-${version}-win64.7z" + println " ${untouchedUrl}" + } + + // Determine filename from URL + def filename = untouchedUrl.substring(untouchedUrl.lastIndexOf('/') + 1) + def downloadDir = file(bundleTmpDownloadPath) + downloadDir.mkdirs() + + def downloadedFile = file("${downloadDir}/${filename}") + + // Download if not already present + if (!downloadedFile.exists()) { + println " Downloading to: ${downloadedFile}" + try { + ant.get(src: untouchedUrl, dest: downloadedFile, verbose: true) + println " Download complete from modules-untouched" + } catch (Exception e) { + throw new GradleException(""" + Failed to download from modules-untouched: ${e.message} + + Tried URL: ${untouchedUrl} + + Please verify: + 1. Version ${version} exists in modules-untouched repository + 2. The URL is correct in bruno.properties or matches format: bruno-{version}/bruno-{version}-win64.7z + 3. You have internet connectivity + """.stripIndent()) + } + } else { + println " Using cached file: ${downloadedFile}" + } + + return downloadedFile +} + +// Function to download and extract Bruno binaries +def downloadAndExtractBruno(String version, File destDir) { + // Load releases.properties to get download URL + def releasesFile = file('releases.properties') + if (!releasesFile.exists()) { + throw new GradleException("releases.properties not found") + } + + def releases = new Properties() + releasesFile.withInputStream { releases.load(it) } + + def downloadUrl = releases.getProperty(version) + def downloadedFile = null + + // Check if version exists in releases.properties + if (!downloadUrl) { + println "Version ${version} not found in releases.properties" + println "Fetching from modules-untouched repository..." + + // Try to download from modules-untouched + downloadedFile = downloadFromModulesUntouched(version, destDir) + } else { + println "Downloading Bruno ${version} from:" + println " ${downloadUrl}" + + // Determine filename from URL + def filename = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1) + def downloadDir = file(bundleTmpDownloadPath) + downloadDir.mkdirs() + + downloadedFile = file("${downloadDir}/${filename}") + + // Download if not already present + if (!downloadedFile.exists()) { + println " Downloading to: ${downloadedFile}" + ant.get(src: downloadUrl, dest: downloadedFile, verbose: true) + println " Download complete" + } else { + println " Using cached file: ${downloadedFile}" + } + } + + // Extract the archive + def extractDir = file(bundleTmpExtractPath) + extractDir.mkdirs() + println " Extracting archive..." + def extractPath = file("${extractDir}/${version}") + if (extractPath.exists()) { + delete extractPath + } + extractPath.mkdirs() + + // Determine filename from downloaded file + def filename = downloadedFile.name + + // Use 7zip or built-in extraction + if (filename.endsWith('.7z')) { + // Try to use 7zip if available + def sevenZipPath = find7ZipExecutable() + if (sevenZipPath) { + def command = [ + sevenZipPath.toString(), + 'x', + downloadedFile.absolutePath.toString(), + "-o${extractPath.absolutePath}".toString(), + '-y' + ] + def process = new ProcessBuilder(command as String[]) + .directory(extractPath) + .redirectErrorStream(true) + .start() + + process.inputStream.eachLine { line -> + if (line.trim()) println " ${line}" + } + + def exitCode = process.waitFor() + if (exitCode != 0) { + throw new GradleException("7zip extraction failed with exit code: ${exitCode}") + } + } else { + throw new GradleException("7zip not found. Please install 7zip or extract manually.") + } + } else if (filename.endsWith('.zip')) { + copy { + from zipTree(downloadedFile) + into extractPath + } + } else { + throw new GradleException("Unsupported archive format: ${filename}") + } + + println " Extraction complete" + + // Find the Bruno directory in the extracted files + def brunoDir = findBrunoDirectory(extractPath) + if (!brunoDir) { + throw new GradleException("Could not find Bruno directory in extracted files") + } + + println " Found Bruno directory: ${brunoDir.name}" + + // If downloaded from modules-untouched, add a note + if (!versionExistsInReleases(version)) { + println "" + println "NOTE: Version ${version} was downloaded from modules-untouched repository" + println " Consider adding it to releases.properties for future builds" + } + + return brunoDir +} + +// Function to find Bruno directory in extracted files +def findBrunoDirectory(File extractPath) { + // Look for directory containing bruno.exe + def brunoDirs = extractPath.listFiles()?.findAll { + it.isDirectory() && file("${it}/bruno.exe").exists() + } + + if (brunoDirs && !brunoDirs.isEmpty()) { + return brunoDirs[0] + } + + // If not found at top level, search one level deep + def foundDir = null + extractPath.listFiles()?.each { dir -> + if (dir.isDirectory() && !foundDir) { + def subDirs = dir.listFiles()?.findAll { + it.isDirectory() && file("${it}/bruno.exe").exists() + } + if (subDirs && !subDirs.isEmpty()) { + foundDir = subDirs[0] + } + } + } + + return foundDir +} + +// ============================================================================ +// GRADLE TASKS +// ============================================================================ + +// Task: Display build information +tasks.register('info') { + group = 'help' + description = 'Display build configuration information' + + // Capture values at configuration time to avoid deprecation warnings + def projectName = project.name + def projectVersion = project.version + def projectDescription = project.description + def projectBasedirValue = projectBasedir + def rootDirValue = rootDir + def devPathValue = devPath + def bundleNameValue = bundleName + def bundleReleaseValue = bundleRelease + def bundleTypeValue = bundleType + def bundleFormatValue = bundleFormat + def buildBasePathValue = buildBasePath + def buildTmpPathValue = buildTmpPath + def bundleTmpPrepPathValue = bundleTmpPrepPath + def bundleTmpBuildPathValue = bundleTmpBuildPath + def bundleTmpSrcPathValue = bundleTmpSrcPath + def bundleTmpDownloadPathValue = bundleTmpDownloadPath + def bundleTmpExtractPathValue = bundleTmpExtractPath + def javaVersion = JavaVersion.current() + def javaHome = System.getProperty('java.home') + def gradleVersion = gradle.gradleVersion + def gradleHome = gradle.gradleHomeDir + + doLast { + println """ + ================================================================ + Bearsampp Module Bruno - Build Info + ================================================================ + + Project: ${projectName} + Version: ${projectVersion} + Description: ${projectDescription} + + Bundle Properties: + Name: ${bundleNameValue} + Release: ${bundleReleaseValue} + Type: ${bundleTypeValue} + Format: ${bundleFormatValue} + + Paths: + Project Dir: ${projectBasedirValue} + Root Dir: ${rootDirValue} + Dev Path: ${devPathValue} + Build Base: ${buildBasePathValue} + Build Tmp: ${buildTmpPathValue} + Tmp Prep: ${bundleTmpPrepPathValue} + Tmp Build: ${bundleTmpBuildPathValue} + Tmp Src: ${bundleTmpSrcPathValue} + Tmp Download: ${bundleTmpDownloadPathValue} + Tmp Extract: ${bundleTmpExtractPathValue} + + Java: + Version: ${javaVersion} + Home: ${javaHome} + + Gradle: + Version: ${gradleVersion} + Home: ${gradleHome} + + Available Task Groups: + * build - Build and package tasks + * help - Help and information tasks + * verification - Verification tasks + + Quick Start: + gradle tasks - List all available tasks + gradle info - Show this information + gradle release -PbundleVersion=2.13.0 - Build specific version + gradle releaseAll - Build all versions + gradle clean - Clean build artifacts + gradle verify - Verify build environment + """.stripIndent() + } +} + +// Task: Main release task - build a specific version +tasks.register('release') { + group = 'build' + description = 'Build release package for a specific version (use -PbundleVersion=X.X.X or run interactively)' + + // Capture property at configuration time to avoid deprecation warning + def versionProperty = project.findProperty('bundleVersion') + + doLast { + def versionToBuild = versionProperty + + if (!versionToBuild) { + // Interactive mode - prompt for version + def availableVersions = getAvailableVersions() + + if (availableVersions.isEmpty()) { + throw new GradleException("No versions found in bin/ directory") + } + + println "" + println "=".multiply(70) + println "Interactive Release Mode" + println "=".multiply(70) + println "" + println "Available versions:" + + // Show versions with location tags + def binDir = file("${projectDir}/bin") + def archivedDir = file("${projectDir}/bin/archived") + + availableVersions.eachWithIndex { version, index -> + def location = "" + if (binDir.exists() && file("${binDir}/${bundleName}${version}").exists()) { + location = "[bin]" + } else if (archivedDir.exists() && file("${archivedDir}/${bundleName}${version}").exists()) { + location = "[bin/archived]" + } + println " ${(index + 1).toString().padLeft(2)}. ${version.padRight(15)} ${location}" + } + println "" + println "Enter version number to build:" + println "" + + // Read input using Gradle's standard input + def input = null + try { + def reader = new BufferedReader(new InputStreamReader(System.in)) + input = reader.readLine() + } catch (Exception e) { + throw new GradleException(""" + Failed to read input. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X + + Available versions: ${availableVersions.join(', ')} + """.stripIndent()) + } + + if (!input || input.trim().isEmpty()) { + throw new GradleException(""" + No version selected. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X + + Available versions: ${availableVersions.join(', ')} + """.stripIndent()) + } + + versionToBuild = input.trim() + + // Validate the entered version + if (!availableVersions.contains(versionToBuild)) { + throw new GradleException(""" + Invalid version: ${versionToBuild} + + Please choose from available versions: + ${availableVersions.collect { " - ${it}" }.join('\n')} + """.stripIndent()) + } + + println "" + println "Selected version: ${versionToBuild}" + } + + println "" + println "=".multiply(70) + println "Building ${bundleName} ${versionToBuild}" + println "=".multiply(70) + println "" + + // Validate version exists - check both bin and bin/archived directories + def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}") + if (!bundlePath.exists()) { + bundlePath = file("${projectDir}/bin/archived/${bundleName}${versionToBuild}") + if (!bundlePath.exists()) { + throw new GradleException("Bundle version not found in bin/ or bin/archived/\n\nAvailable versions:\n${getAvailableVersions().collect { " - ${it}" }.join('\n')}") + } + } + + println "Bundle path: ${bundlePath}" + println "" + + // Get the bundle folder and version + def bundleFolder = bundlePath.name + def bundleVersion = bundleFolder.replace(bundleName, '') + + // Determine source paths + def bundleSrcDest = bundlePath + def bundleSrcFinal = bundleSrcDest + + // Check if bruno.exe exists in bin/ directory + def brunoExe = file("${bundleSrcFinal}/bruno.exe") + if (!brunoExe.exists()) { + // Bruno binaries not found in bin/ - check if already downloaded to bearsampp-build/tmp + def tmpExtractPath = file("${bundleTmpExtractPath}/${bundleVersion}") + def tmpBrunoDir = findBrunoDirectory(tmpExtractPath) + + if (tmpBrunoDir && tmpBrunoDir.exists()) { + println "Using cached Bruno binaries from bearsampp-build/tmp" + bundleSrcFinal = tmpBrunoDir + } else { + // Download and extract to bearsampp-build/tmp + println "" + println "Bruno binaries not found" + println "Downloading Bruno ${bundleVersion}..." + println "" + + try { + // Download and extract to bearsampp-build/tmp + bundleSrcFinal = downloadAndExtractBruno(bundleVersion, file(bundleTmpExtractPath)) + } catch (Exception e) { + throw new GradleException(""" + Failed to download Bruno binaries: ${e.message} + + You can manually download and extract Bruno binaries to: + ${bundleSrcDest}/ + + Or check that version ${bundleVersion} exists in releases.properties + """.stripIndent()) + } + } + } + + // Verify bruno.exe exists + brunoExe = file("${bundleSrcFinal}/bruno.exe") + if (!brunoExe.exists()) { + throw new GradleException("bruno.exe not found at ${brunoExe}") + } + + println "Source folder: ${bundleSrcFinal}" + println "" + + // Prepare output directory + def brunoPrepPath = file("${bundleTmpPrepPath}/${bundleName}${bundleVersion}") + if (brunoPrepPath.exists()) { + delete brunoPrepPath + } + brunoPrepPath.mkdirs() + + // Copy Bruno binaries from extracted/downloaded location + println "Copying Bruno files..." + copy { + from bundleSrcFinal + into brunoPrepPath + } + + // Copy configuration files from bin directory + println "Copying configuration files..." + copy { + from bundleSrcDest + into brunoPrepPath + include 'bearsampp.conf' + } + + println "" + println "Preparing archive..." + + // Determine build output path following Apache pattern + // bearsampp-build/tools/bruno/{bundleRelease} for tools + // bearsampp-build/bins/apache/{bundleRelease} for bins + def buildPath = file(buildBasePath) + def buildBinsPath = file("${buildPath}/${bundleType}/${bundleName}/${bundleRelease}") + buildBinsPath.mkdirs() + + // Build archive filename + def destFile = file("${buildBinsPath}/bearsampp-${bundleName}-${bundleVersion}-${bundleRelease}") + + // Compress based on format + if (bundleFormat == '7z') { + // 7z format + def archiveFile = file("${destFile}.7z") + if (archiveFile.exists()) { + delete archiveFile + } + + println "Compressing ${bundleName}${bundleVersion} to ${archiveFile.name}..." + + // Find 7z executable + def sevenZipExe = find7ZipExecutable() + if (!sevenZipExe) { + throw new GradleException("7-Zip not found. Please install 7-Zip or set 7Z_HOME environment variable.") + } + + println "Using 7-Zip: ${sevenZipExe}" + + // Create 7z archive + def command = [ + sevenZipExe, + 'a', + '-t7z', + archiveFile.absolutePath.toString(), + '.' + ] + + def process = new ProcessBuilder(command as String[]) + .directory(brunoPrepPath) + .redirectErrorStream(true) + .start() + + process.inputStream.eachLine { line -> + if (line.trim()) println " ${line}" + } + + def exitCode = process.waitFor() + if (exitCode != 0) { + throw new GradleException("7zip compression failed with exit code: ${exitCode}") + } + + println "Archive created: ${archiveFile}" + + // Generate hash files + println "Generating hash files..." + generateHashFiles(archiveFile) + + } else { + // ZIP format + def archiveFile = file("${destFile}.zip") + if (archiveFile.exists()) { + delete archiveFile + } + + println "Compressing ${bundleName}${bundleVersion} to ${archiveFile.name}..." + + ant.zip(destfile: archiveFile, basedir: brunoPrepPath) + + println "Archive created: ${archiveFile}" + + // Generate hash files + println "Generating hash files..." + generateHashFiles(archiveFile) + } + + println "" + println "=".multiply(70) + println "[SUCCESS] Release build completed successfully for version ${versionToBuild}" + println "Output directory: ${buildBinsPath}" + println "Archive: ${destFile}.${bundleFormat}" + println "=".multiply(70) + } +} + +// Helper function to find 7-Zip executable +def find7ZipExecutable() { + // Check environment variable + def sevenZipHome = System.getenv('7Z_HOME') + if (sevenZipHome) { + def exe = file("${sevenZipHome}/7z.exe") + if (exe.exists()) { + return exe.absolutePath + } + } + + // Check common installation paths + def commonPaths = [ + 'C:/Program Files/7-Zip/7z.exe', + 'C:/Program Files (x86)/7-Zip/7z.exe', + 'D:/Program Files/7-Zip/7z.exe', + 'D:/Program Files (x86)/7-Zip/7z.exe' + ] + + for (path in commonPaths) { + def exe = file(path) + if (exe.exists()) { + return exe.absolutePath + } + } + + // Try to find in PATH + try { + def process = ['where', '7z.exe'].execute() + process.waitFor() + if (process.exitValue() == 0) { + def output = process.text.trim() + if (output) { + return output.split('\n')[0].trim() + } + } + } catch (Exception e) { + // Ignore + } + + return null +} + +// Helper function to generate hash files +def generateHashFiles(File file) { + if (!file.exists()) { + throw new GradleException("File not found for hashing: ${file}") + } + + // Generate MD5 + def md5File = new File("${file.absolutePath}.md5") + def md5Hash = calculateHash(file, 'MD5') + md5File.text = "${md5Hash} ${file.name}\n" + println " Created: ${md5File.name}" + + // Generate SHA1 + def sha1File = new File("${file.absolutePath}.sha1") + def sha1Hash = calculateHash(file, 'SHA-1') + sha1File.text = "${sha1Hash} ${file.name}\n" + println " Created: ${sha1File.name}" + + // Generate SHA256 + def sha256File = new File("${file.absolutePath}.sha256") + def sha256Hash = calculateHash(file, 'SHA-256') + sha256File.text = "${sha256Hash} ${file.name}\n" + println " Created: ${sha256File.name}" + + // Generate SHA512 + def sha512File = new File("${file.absolutePath}.sha512") + def sha512Hash = calculateHash(file, 'SHA-512') + sha512File.text = "${sha512Hash} ${file.name}\n" + println " Created: ${sha512File.name}" +} + +// Helper function to calculate hash +def calculateHash(File file, String algorithm) { + def digest = java.security.MessageDigest.getInstance(algorithm) + file.withInputStream { stream -> + def buffer = new byte[8192] + def bytesRead + while ((bytesRead = stream.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead) + } + } + return digest.digest().collect { String.format('%02x', it) }.join('') +} + +// Helper function to get available versions +def getAvailableVersions() { + def versions = [] + + // Check bin directory + def binDir = file("${projectDir}/bin") + if (binDir.exists()) { + def binVersions = binDir.listFiles() + ?.findAll { it.isDirectory() && it.name.startsWith(bundleName) && it.name != 'archived' } + ?.collect { it.name.replace(bundleName, '') } ?: [] + versions.addAll(binVersions) + } + + // Check bin/archived subdirectory + def archivedDir = file("${projectDir}/bin/archived") + if (archivedDir.exists()) { + def archivedVersions = archivedDir.listFiles() + ?.findAll { it.isDirectory() && it.name.startsWith(bundleName) } + ?.collect { it.name.replace(bundleName, '') } ?: [] + versions.addAll(archivedVersions) + } + + // Remove duplicates and sort + return versions.unique().sort() +} + +// Task: Build all available versions +tasks.register('releaseAll') { + group = 'build' + description = 'Build release packages for all available versions in bin/ directory' + + doLast { + def binDir = file("${projectDir}/bin") + if (!binDir.exists()) { + throw new GradleException("bin/ directory not found") + } + + def versions = getAvailableVersions() + + if (versions.isEmpty()) { + throw new GradleException("No versions found in bin/ directory") + } + + println "" + println "=".multiply(70) + println "Building releases for ${versions.size()} ${bundleName} versions" + println "=".multiply(70) + println "" + + def successCount = 0 + def failedVersions = [] + + versions.each { version -> + println "=".multiply(70) + println "[${successCount + 1}/${versions.size()}] Building ${bundleName} ${version}..." + println "=".multiply(70) + + try { + // Call the release task logic for this version + def bundlePath = file("${projectDir}/bin/${bundleName}${version}") + + if (!bundlePath.exists()) { + throw new GradleException("Bundle path not found: ${bundlePath}") + } + + println "Bundle path: ${bundlePath}" + println "" + + // Get the bundle folder and version + def bundleFolder = bundlePath.name + def bundleVersion = bundleFolder.replace(bundleName, '') + + // Determine source paths + def bundleSrcDest = bundlePath + def bundleSrcFinal = bundleSrcDest + + // Check if bruno.exe exists + def brunoExe = file("${bundleSrcFinal}/bruno.exe") + if (!brunoExe.exists()) { + throw new GradleException("bruno.exe not found at ${brunoExe}") + } + + println "Source folder: ${bundleSrcFinal}" + println "" + + // Prepare output directory + def brunoPrepPath = file("${bundleTmpPrepPath}/${bundleName}${bundleVersion}") + if (brunoPrepPath.exists()) { + delete brunoPrepPath + } + brunoPrepPath.mkdirs() + + // Copy Bruno files + println "Copying Bruno files..." + copy { + from bundleSrcDest + into brunoPrepPath + } + + println "" + println "[SUCCESS] ${bundleName} ${version} completed" + println "Output: ${brunoPrepPath}" + successCount++ + + } catch (Exception e) { + println "" + println "[FAILED] ${bundleName} ${version}: ${e.message}" + failedVersions.add(version) + } + + println "" + } + + // Summary + println "=".multiply(70) + println "Build Summary" + println "=".multiply(70) + println "Total versions: ${versions.size()}" + println "Successful: ${successCount}" + println "Failed: ${failedVersions.size()}" + + if (!failedVersions.isEmpty()) { + println "" + println "Failed versions:" + failedVersions.each { v -> + println " - ${v}" + } + } + + println "=".multiply(70) + + if (failedVersions.isEmpty()) { + println "[SUCCESS] All versions built successfully!" + } else { + throw new GradleException("${failedVersions.size()} version(s) failed to build") + } + } +} + +// Task: Enhanced clean task +tasks.named('clean') { + group = 'build' + description = 'Clean build artifacts and temporary files' + + doLast { + // Clean Gradle build directory + def buildDir = file("${projectDir}/build") + if (buildDir.exists()) { + delete buildDir + } + + println "[SUCCESS] Build artifacts cleaned" + } +} + +// Task: Verify build environment +tasks.register('verify') { + group = 'verification' + description = 'Verify build environment and dependencies' + + doLast { + println "Verifying build environment for module-bruno..." + + def checks = [:] + + // Check Java version + def javaVersion = JavaVersion.current() + checks['Java 8+'] = javaVersion >= JavaVersion.VERSION_1_8 + + // Check required files + checks['build.properties'] = file('build.properties').exists() + checks['releases.properties'] = file('releases.properties').exists() + + // Check dev directory + checks['dev directory'] = file(devPath).exists() + + // Check bin directory + checks['bin directory'] = file("${projectDir}/bin").exists() + + // Check 7-Zip if format is 7z + if (bundleFormat == '7z') { + checks['7-Zip'] = find7ZipExecutable() != null + } + + println "\nEnvironment Check Results:" + println "-".multiply(60) + checks.each { name, passed -> + def status = passed ? "[PASS]" : "[FAIL]" + println " ${status.padRight(10)} ${name}" + } + println "-".multiply(60) + + def allPassed = checks.values().every { it } + if (allPassed) { + println "\n[SUCCESS] All checks passed! Build environment is ready." + println "\nYou can now run:" + println " gradle release -PbundleVersion=2.13.0 - Build release for version" + println " gradle listVersions - List available versions" + } else { + println "\n[WARNING] Some checks failed. Please review the requirements." + throw new GradleException("Build environment verification failed") + } + } +} + +// Task: List all bundle versions from releases.properties +tasks.register('listReleases') { + group = 'help' + description = 'List all available releases from releases.properties' + + doLast { + def releasesFile = file('releases.properties') + if (!releasesFile.exists()) { + println "releases.properties not found" + return + } + + def releases = new Properties() + releasesFile.withInputStream { releases.load(it) } + + println "\nAvailable Bruno Releases:" + println "-".multiply(80) + releases.sort { it.key }.each { version, url -> + println " ${version.padRight(10)} -> ${url}" + } + println "-".multiply(80) + println "Total releases: ${releases.size()}" + } +} + +// Task: List available bundle versions in bin and bin/archived directories +tasks.register('listVersions') { + group = 'help' + description = 'List all available bundle versions in bin/ and bin/archived/ directories' + + doLast { + def versions = getAvailableVersions() + + if (versions.isEmpty()) { + println "\nNo versions found in bin/ or bin/archived/ directories" + return + } + + println "\nAvailable ${bundleName} versions:" + println "-".multiply(60) + + // Show which directory each version is in + def binDir = file("${projectDir}/bin") + def archivedDir = file("${projectDir}/bin/archived") + + versions.each { version -> + def location = "" + if (binDir.exists() && file("${binDir}/${bundleName}${version}").exists()) { + location = "[bin]" + } else if (archivedDir.exists() && file("${archivedDir}/${bundleName}${version}").exists()) { + location = "[bin/archived]" + } + println " ${version.padRight(15)} ${location}" + } + println "-".multiply(60) + println "Total versions: ${versions.size()}" + + if (!versions.isEmpty()) { + println "\nTo build a specific version:" + println " gradle release -PbundleVersion=${versions.last()}" + } + } +} + +// Task: Validate build.properties +tasks.register('validateProperties') { + group = 'verification' + description = 'Validate build.properties configuration' + + doLast { + println "Validating build.properties..." + + def required = ['bundle.name', 'bundle.release', 'bundle.type', 'bundle.format'] + def missing = [] + + required.each { prop -> + if (!buildProps.containsKey(prop) || buildProps.getProperty(prop).trim().isEmpty()) { + missing.add(prop) + } + } + + if (missing.isEmpty()) { + println "[SUCCESS] All required properties are present:" + required.each { prop -> + println " ${prop} = ${buildProps.getProperty(prop)}" + } + } else { + println "[ERROR] Missing required properties:" + missing.each { prop -> + println " - ${prop}" + } + throw new GradleException("build.properties validation failed") + } + } +} + +// Task: Check modules-untouched integration +tasks.register('checkModulesUntouched') { + group = 'verification' + description = 'Check modules-untouched repository integration and available versions' + + doLast { + println "" + println "=".multiply(70) + println "Modules-Untouched Integration Check" + println "=".multiply(70) + println "" + + def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/bruno.properties" + println "Repository URL:" + println " ${propsUrl}" + println "" + + println "Fetching bruno.properties from modules-untouched..." + def untouchedProps = fetchModulesUntouchedProperties() + + if (untouchedProps) { + println "" + println "=".multiply(70) + println "Available Versions in modules-untouched" + println "=".multiply(70) + + def sortedVersions = untouchedProps.sort { a, b -> + // Simple version comparison + def aParts = a.key.tokenize('.') + def bParts = b.key.tokenize('.') + for (int i = 0; i < Math.min(aParts.size(), bParts.size()); i++) { + def aNum = aParts[i].toInteger() + def bNum = bParts[i].toInteger() + if (aNum != bNum) return aNum <=> bNum + } + return aParts.size() <=> bParts.size() + } + + sortedVersions.each { version, url -> + def inReleases = versionExistsInReleases(version) ? "[in releases.properties]" : "" + println " ${version.padRight(10)} ${inReleases}" + } + + println "=".multiply(70) + println "Total versions: ${untouchedProps.size()}" + println "" + + // Compare with releases.properties + def releasesFile = file('releases.properties') + if (releasesFile.exists()) { + def releases = new Properties() + releasesFile.withInputStream { releases.load(it) } + + def onlyInUntouched = untouchedProps.keySet() - releases.keySet() + def onlyInReleases = releases.keySet() - untouchedProps.keySet() + + if (!onlyInUntouched.isEmpty()) { + println "Versions only in modules-untouched (${onlyInUntouched.size()}):" + onlyInUntouched.sort().each { version -> + println " - ${version}" + } + println "" + } + + if (!onlyInReleases.isEmpty()) { + println "Versions only in releases.properties (${onlyInReleases.size()}):" + onlyInReleases.sort().each { version -> + println " - ${version}" + } + println "" + } + + def inBoth = untouchedProps.keySet().intersect(releases.keySet()) + println "Versions in both sources: ${inBoth.size()}" + } + + println "" + println "=".multiply(70) + println "[SUCCESS] modules-untouched integration is working" + println "=".multiply(70) + println "" + println "Version Resolution Strategy:" + println " 1. Check releases.properties (local)" + println " 2. Check modules-untouched bruno.properties (remote)" + println " 3. Construct standard URL format (fallback)" + println "" + println "Documentation: /.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md" + + } else { + println "" + println "=".multiply(70) + println "[WARNING] Could not fetch bruno.properties from modules-untouched" + println "=".multiply(70) + println "" + println "This may be due to:" + println " - Network connectivity issues" + println " - Repository access problems" + println " - File not available at expected location" + println "" + println "The build system will fall back to:" + println " 1. releases.properties (if version exists)" + println " 2. Standard URL format construction" + } + } +} + +// ============================================================================ +// BUILD LIFECYCLE HOOKS +// ============================================================================ + +gradle.taskGraph.whenReady { graph -> + println """ + ================================================================ + Bearsampp Module Bruno - Gradle Build + ================================================================ + """.stripIndent() +} + +// ============================================================================ +// DEFAULT TASK +// ============================================================================ + +defaultTasks 'info' diff --git a/build.xml b/build.xml deleted file mode 100644 index 548793a..0000000 --- a/build.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gradle.properties b/gradle.properties index 19d23f2..05bb175 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,19 +1,30 @@ -# Gradle Build Properties for Bearsampp Module Memcached +# Bearsampp Module Memcached - Gradle Properties +# +# Gradle build configuration for optimal performance -# Gradle daemon configuration +# Enable Gradle daemon for faster builds org.gradle.daemon=true + +# Enable parallel execution org.gradle.parallel=true + +# Enable build caching org.gradle.caching=true -# JVM settings for Gradle -org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError +# Enable configuration on demand +org.gradle.configureondemand=true + +# JVM arguments for Gradle daemon +org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m + +# Console output +org.gradle.console=rich -# Configure console output -org.gradle.console=auto +# Warning mode org.gradle.warning.mode=all -# Build performance -org.gradle.configureondemand=false +# File system watching (Gradle 7.0+) +org.gradle.vfs.watch=true -# Gradle version compatibility -# This project is compatible with Gradle 7.0+ +# Build scan (optional) +# org.gradle.scan=true diff --git a/settings.gradle b/settings.gradle index 37cce3a..64c45b3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,25 +1,10 @@ -/* - * Bearsampp Module Memcached - Gradle Settings - */ - rootProject.name = 'module-memcached' -// Enable Gradle features for better performance enableFeaturePreview('STABLE_CONFIGURATION_CACHE') -// Configure build cache for faster builds buildCache { local { enabled = true directory = file("${rootDir}/.gradle/build-cache") } } - -// Display initialization message -gradle.rootProject { - println """ - ================================================================ - Initializing Bearsampp Module Memcached Build - ================================================================ - """.stripIndent() -} From 29c1abd6ee101d43453f5e707fc44bc09d055e40 Mon Sep 17 00:00:00 2001 From: jwaisner Date: Fri, 14 Nov 2025 20:49:03 -0600 Subject: [PATCH 3/7] Add comprehensive Gradle build documentation and reorganize archived versions --- .gradle-docs/CONFIGURATION_SUMMARY.md | 271 +++++++ .gradle-docs/FEATURE_SUMMARY.md | 111 +++ .gradle-docs/INTERACTIVE_MODE.md | 275 +++++++ .gradle-docs/MIGRATION_SUMMARY.md | 248 ++++++ .gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md | 221 ++++++ .gradle-docs/QUICK_REFERENCE.md | 99 +++ .gradle-docs/README.md | 81 ++ README.md | 76 ++ .../memcached1.6.15/bearsampp.conf | 0 .../memcached1.6.17/bearsampp.conf | 0 .../memcached1.6.18/bearsampp.conf | 0 .../memcached1.6.21/bearsampp.conf | 0 .../memcached1.6.24/bearsampp.conf | 0 .../memcached1.6.29/bearsampp.conf | 0 .../memcached1.6.31/bearsampp.conf | 0 .../memcached1.6.32/bearsampp.conf | 0 .../memcached1.6.33/bearsampp.conf | 0 .../memcached1.6.36/bearsampp.conf | 0 .../memcached1.6.37/bearsampp.conf | 0 .../memcached1.6.37/memcached.exe | Bin .../memcached1.6.38/bearsampp.conf | 0 build.gradle | 719 +++++++++++++++--- 22 files changed, 1986 insertions(+), 115 deletions(-) create mode 100644 .gradle-docs/CONFIGURATION_SUMMARY.md create mode 100644 .gradle-docs/FEATURE_SUMMARY.md create mode 100644 .gradle-docs/INTERACTIVE_MODE.md create mode 100644 .gradle-docs/MIGRATION_SUMMARY.md create mode 100644 .gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md create mode 100644 .gradle-docs/QUICK_REFERENCE.md create mode 100644 .gradle-docs/README.md rename bin/{ => archived}/memcached1.6.15/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.17/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.18/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.21/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.24/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.29/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.31/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.32/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.33/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.36/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.37/bearsampp.conf (100%) rename bin/{ => archived}/memcached1.6.37/memcached.exe (100%) rename bin/{ => archived}/memcached1.6.38/bearsampp.conf (100%) diff --git a/.gradle-docs/CONFIGURATION_SUMMARY.md b/.gradle-docs/CONFIGURATION_SUMMARY.md new file mode 100644 index 0000000..c276c6f --- /dev/null +++ b/.gradle-docs/CONFIGURATION_SUMMARY.md @@ -0,0 +1,271 @@ +# Configuration Summary + +## Overview + +The Bearsampp Memcached module build system is configured through the `build.properties` file and various Gradle project settings. + +## build.properties + +The main configuration file for the build system. + +### Location +``` +D:/Bearsampp-dev/module-memcached/build.properties +``` + +### Properties + +#### bundle.name +- **Type**: String +- **Default**: `memcached` +- **Description**: The name of the bundle/module +- **Usage**: Used in directory names, archive names, and output paths + +```properties +bundle.name = memcached +``` + +#### bundle.release +- **Type**: Version String +- **Default**: `2025.8.20` +- **Description**: The release version of the build configuration +- **Usage**: Project version, metadata + +```properties +bundle.release = 2025.8.20 +``` + +#### bundle.type +- **Type**: String +- **Default**: `bins` +- **Options**: `bins`, `tools`, `apps` +- **Description**: The type of bundle being built +- **Usage**: Categorization, metadata + +```properties +bundle.type = bins +``` + +#### bundle.format +- **Type**: String +- **Default**: `7z` +- **Options**: `7z`, `zip` +- **Description**: Archive format for release packages +- **Usage**: Determines compression method and file extension + +```properties +bundle.format = 7z +``` + +#### build.path (Optional) +- **Type**: Path String +- **Default**: `C:/Bearsampp-build` +- **Description**: Output directory for built releases +- **Usage**: Destination for release archives and hash files + +```properties +#build.path = C:/Bearsampp-build +``` + +## Gradle Configuration + +### Project Settings + +Defined in `build.gradle`: + +```groovy +group = 'com.bearsampp.modules' +version = buildProps.getProperty('bundle.release', '1.0.0') +description = "Bearsampp Module - ${buildProps.getProperty('bundle.name', 'memcached')}" +``` + +### Path Configuration + +```groovy +ext { + projectBasedir = projectDir.absolutePath + rootDir = projectDir.parent + devPath = file("${rootDir}/dev").absolutePath + buildPropertiesFile = file('build.properties').absolutePath + + // Bundle properties from build.properties + bundleName = buildProps.getProperty('bundle.name', 'memcached') + bundleRelease = buildProps.getProperty('bundle.release', '1.0.0') + bundleType = buildProps.getProperty('bundle.type', 'bins') + bundleFormat = buildProps.getProperty('bundle.format', '7z') + + // Build paths + bundleBuildPath = buildProps.getProperty('build.path', 'C:/Bearsampp-build') + bundleTmpPrepPath = "${bundleBuildPath}/tmp/prep" +} +``` + +## Directory Structure + +### Source Directories + +``` +D:/Bearsampp-dev/module-memcached/ +├── bin/ # Version directories +│ ├── memcached1.6.15/ +│ ├── memcached1.6.17/ +│ └── ... +└── bin/archived/ # Archived versions + ├── memcached1.6.10/ + └── ... +``` + +### Build Output Structure + +``` +C:/Bearsampp-build/ +├── memcached1.6.29/ +│ ├── memcached1.6.29.7z +│ ├── memcached1.6.29.7z.md5 +│ ├── memcached1.6.29.7z.sha1 +│ ├── memcached1.6.29.7z.sha256 +│ └── memcached1.6.29.7z.sha512 +└── tmp/ + └── prep/ + └── memcached1.6.29/ # Staging directory +``` + +## Environment Variables + +### 7Z_HOME (Optional) + +- **Description**: Path to 7-Zip installation directory +- **Usage**: Used to locate 7z.exe if not in standard locations +- **Example**: `C:/Program Files/7-Zip` + +```bash +# Windows +set 7Z_HOME=C:\Program Files\7-Zip + +# PowerShell +$env:7Z_HOME = "C:\Program Files\7-Zip" +``` + +## Task Configuration + +### Default Task + +```groovy +defaultTasks 'info' +``` + +Running `gradle` without arguments executes the `info` task. + +### Task Groups + +Tasks are organized into groups: + +- **build**: Build and package tasks +- **help**: Information and help tasks +- **verification**: Validation and verification tasks + +## Validation + +### Required Properties + +The following properties must be present in `build.properties`: + +- `bundle.name` +- `bundle.release` +- `bundle.type` +- `bundle.format` + +### Validation Command + +```bash +gradle validateProperties +``` + +This checks that all required properties are present and non-empty. + +## Customization + +### Changing Build Output Directory + +Edit `build.properties`: + +```properties +build.path = D:/MyCustomBuildPath +``` + +### Changing Archive Format + +Edit `build.properties`: + +```properties +bundle.format = zip +``` + +### Changing Bundle Type + +Edit `build.properties`: + +```properties +bundle.type = tools +``` + +## Best Practices + +### 1. Version Control + +- Commit `build.properties` to version control +- Document any custom settings +- Use comments for optional properties + +### 2. Path Configuration + +- Use absolute paths for `build.path` +- Ensure build directory has write permissions +- Keep build output separate from source + +### 3. Archive Format + +- Use `7z` for maximum compression +- Use `zip` for better compatibility +- Consider target platform requirements + +### 4. Validation + +- Run `gradle validateProperties` before builds +- Run `gradle verify` to check environment +- Test configuration changes + +## Troubleshooting + +### Property Not Found + +If you get "property not found" errors: + +1. Check `build.properties` exists +2. Verify property names are correct +3. Ensure no typos in property keys +4. Run `gradle validateProperties` + +### Path Issues + +If you get path-related errors: + +1. Check paths use forward slashes or escaped backslashes +2. Verify directories exist and are accessible +3. Check file permissions +4. Use absolute paths when possible + +### Format Issues + +If archive creation fails: + +1. Verify `bundle.format` is `7z` or `zip` +2. Check 7-Zip is installed (for 7z format) +3. Run `gradle verify` to check environment +4. Check available disk space + +## Related Documentation + +- **QUICK_REFERENCE.md** - Common commands and usage +- **FEATURE_SUMMARY.md** - Build system features +- **MODULES_UNTOUCHED_INTEGRATION.md** - Remote integration details diff --git a/.gradle-docs/FEATURE_SUMMARY.md b/.gradle-docs/FEATURE_SUMMARY.md new file mode 100644 index 0000000..2766014 --- /dev/null +++ b/.gradle-docs/FEATURE_SUMMARY.md @@ -0,0 +1,111 @@ +# Feature Summary + +## Overview + +The Bearsampp Memcached module uses a modern Gradle build system that provides comprehensive build automation, version management, and release packaging capabilities. + +## Key Features + +### 1. Native Gradle Build + +- **No Ant Dependency**: Pure Gradle implementation without legacy Ant integration +- **Modern Build System**: Leverages Gradle's caching, incremental builds, and parallel execution +- **Clean Architecture**: Well-organized task structure with clear separation of concerns + +### 2. Automatic Hash File Generation + +For each release archive, the build system automatically generates: +- **MD5** hash file (`.md5`) +- **SHA1** hash file (`.sha1`) +- **SHA256** hash file (`.sha256`) +- **SHA512** hash file (`.sha512`) + +This ensures integrity verification for all distributed packages. + +### 3. Modules-Untouched Integration + +- **Remote Version Fetching**: Automatically fetches available versions from the modules-untouched repository +- **Fallback Strategy**: Uses standard URL construction if remote fetch fails +- **Version Discovery**: Lists all available releases from the remote repository + +### 4. Flexible Version Management + +- **Multiple Source Directories**: Supports both `bin/` and `bin/archived/` directories +- **Version Discovery**: Automatically detects all available versions +- **Batch Building**: Build all versions with a single command + +### 5. Archive Format Support + +- **7-Zip Format**: Native support for `.7z` archives with maximum compression +- **ZIP Format**: Built-in Gradle ZIP support as alternative +- **Configurable**: Format can be changed in `build.properties` + +### 6. Comprehensive Verification + +- **Environment Checks**: Verifies Java version, required files, and dependencies +- **Configuration Validation**: Ensures all required properties are present +- **Integration Testing**: Tests modules-untouched connectivity + +### 7. Rich Task Set + +#### Build Tasks +- `release` - Build single version release +- `releaseAll` - Build all available versions +- `clean` - Clean build artifacts + +#### Help Tasks +- `info` - Display build configuration +- `listVersions` - List local versions +- `listReleases` - List remote versions +- `tasks` - List all available tasks + +#### Verification Tasks +- `verify` - Verify build environment +- `validateProperties` - Validate configuration +- `checkModulesUntouched` - Test remote integration + +### 8. User-Friendly Output + +- **Progress Indicators**: Clear progress messages during builds +- **Formatted Output**: Well-formatted tables and summaries +- **Error Messages**: Helpful error messages with suggestions +- **Build Summaries**: Detailed success/failure reports + +### 9. Configurable Build Paths + +- **Custom Build Directory**: Configurable output directory +- **Temporary Prep Directory**: Separate staging area for builds +- **Organized Output**: Structured output with version-specific directories + +### 10. Cross-Platform Compatibility + +- **Windows Support**: Native Windows path handling +- **PowerShell Compatible**: Works with PowerShell and CMD +- **Path Flexibility**: Handles various drive letters and path formats + +## Benefits + +### For Developers +- **Easy to Use**: Simple command-line interface +- **Fast Builds**: Gradle's incremental build support +- **Reliable**: Comprehensive error checking and validation + +### For Release Management +- **Automated Hashing**: No manual hash file creation +- **Batch Processing**: Build multiple versions efficiently +- **Version Tracking**: Clear version management and discovery + +### For Quality Assurance +- **Integrity Verification**: Multiple hash algorithms +- **Environment Validation**: Pre-build checks +- **Consistent Output**: Standardized release packages + +## Future Enhancements + +Potential areas for future improvement: +- Parallel version building +- Custom compression levels +- Additional archive formats +- Build caching optimization +- Release notes generation +- Automated testing integration diff --git a/.gradle-docs/INTERACTIVE_MODE.md b/.gradle-docs/INTERACTIVE_MODE.md new file mode 100644 index 0000000..94d871d --- /dev/null +++ b/.gradle-docs/INTERACTIVE_MODE.md @@ -0,0 +1,275 @@ +# Interactive Mode + +## Overview + +The `gradle release` task supports both interactive and non-interactive modes, allowing you to choose the most convenient method for building releases. + +## Non-Interactive Mode + +Specify the version directly on the command line: + +```bash +gradle release -PbundleVersion=1.6.29 +``` + +**Use when:** +- Running in CI/CD pipelines +- Automating builds with scripts +- You know exactly which version to build + +## Interactive Mode + +Run without specifying a version to enter interactive mode: + +```bash +gradle release +``` + +### Interactive Mode Flow + +1. **Display Available Versions** + ``` + ====================================================================== + Interactive Release Mode + ====================================================================== + + Available versions: + 1. 1.6.15 [bin] + 2. 1.6.17 [bin] + 3. 1.6.18 [bin] + 4. 1.6.21 [bin] + 5. 1.6.24 [bin] + 6. 1.6.29 [bin] + 7. 1.6.31 [bin] + 8. 1.6.32 [bin] + 9. 1.6.33 [bin] + 10. 1.6.36 [bin] + 11. 1.6.37 [bin] + 12. 1.6.38 [bin] + 13. 1.6.39 [bin] + + Enter version to build (index or version string): + ``` + +2. **Select Version** + + You can select a version in two ways: + + **Option A: By Index Number** + ``` + Enter version to build (index or version string): + 5 + ``` + This will select version 1.6.24 (the 5th in the list) + + **Option B: By Version String** + ``` + Enter version to build (index or version string): + 1.6.29 + ``` + This will select version 1.6.29 directly + +3. **Confirmation** + ``` + Selected version: 1.6.29 + + ====================================================================== + Building memcached 1.6.29 + ====================================================================== + ``` + +4. **Build Process** + The build proceeds normally with the selected version. + +## Features + +### Version Location Tags + +Each version shows where it's located: +- `[bin]` - Located in `bin/` directory +- `[bin/archived]` - Located in `bin/archived/` directory + +This helps you understand which versions are current and which are archived. + +### Input Validation + +The interactive mode validates your input: + +**Invalid Index:** +``` +Enter version to build (index or version string): +99 + +ERROR: Invalid selection index: 99 +Please choose a number between 1 and 13 or enter a version string. +``` + +**Invalid Version String:** +``` +Enter version to build (index or version string): +1.6.99 + +ERROR: Invalid version: 1.6.99 +Please choose from available versions: + - 1.6.15 + - 1.6.17 + ... +``` + +**Empty Input:** +``` +Enter version to build (index or version string): +[Enter] + +ERROR: No version selected. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X + +Available versions: 1.6.15, 1.6.17, 1.6.18, ... +``` + +### Fallback to Non-Interactive + +If interactive input fails (e.g., running in a non-interactive environment), the error message provides clear instructions for using non-interactive mode: + +``` +ERROR: Failed to read input. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X + +Available versions: 1.6.15, 1.6.17, 1.6.18, ... +``` + +## Use Cases + +### Interactive Mode is Best For: + +1. **Manual Builds** + - Building releases locally + - Testing different versions + - Exploring available versions + +2. **Development** + - Quick iteration during development + - Testing build process + - Verifying version availability + +3. **Learning** + - Understanding what versions are available + - Seeing the directory structure + - Getting familiar with the build system + +### Non-Interactive Mode is Best For: + +1. **Automation** + - CI/CD pipelines + - Build scripts + - Scheduled builds + +2. **Scripting** + - Batch processing + - Integration with other tools + - Automated testing + +3. **Remote Execution** + - SSH sessions without TTY + - Background jobs + - Cron jobs + +## PowerShell Note + +When using PowerShell with non-interactive mode, quote the parameter: + +```powershell +# Correct +gradle release "-PbundleVersion=1.6.29" + +# Incorrect (PowerShell interprets -P as a parameter) +gradle release -PbundleVersion=1.6.29 +``` + +In CMD, quotes are optional: + +```cmd +gradle release -PbundleVersion=1.6.29 +``` + +## Examples + +### Example 1: Interactive Selection by Index + +```bash +$ gradle release + +====================================================================== +Interactive Release Mode +====================================================================== + +Available versions: + 1. 1.6.29 [bin] + 2. 1.6.31 [bin] + 3. 1.6.32 [bin] + +Enter version to build (index or version string): +2 + +Selected version: 1.6.31 + +====================================================================== +Building memcached 1.6.31 +====================================================================== +... +``` + +### Example 2: Interactive Selection by Version String + +```bash +$ gradle release + +====================================================================== +Interactive Release Mode +====================================================================== + +Available versions: + 1. 1.6.29 [bin] + 2. 1.6.31 [bin] + 3. 1.6.32 [bin] + +Enter version to build (index or version string): +1.6.32 + +Selected version: 1.6.32 + +====================================================================== +Building memcached 1.6.32 +====================================================================== +... +``` + +### Example 3: Non-Interactive Mode + +```bash +$ gradle release -PbundleVersion=1.6.29 + +====================================================================== +Building memcached 1.6.29 +====================================================================== +... +``` + +## Comparison with Bruno Module + +The interactive mode implementation matches the bruno module's behavior: + +✅ Same interactive prompt format +✅ Same version selection methods (index or string) +✅ Same validation logic +✅ Same error messages +✅ Same fallback behavior +✅ Same location tags display + +This ensures consistency across all Bearsampp modules. + +## Related Documentation + +- **QUICK_REFERENCE.md** - Quick command reference +- **FEATURE_SUMMARY.md** - Overview of all features +- **README.md** - Main documentation diff --git a/.gradle-docs/MIGRATION_SUMMARY.md b/.gradle-docs/MIGRATION_SUMMARY.md new file mode 100644 index 0000000..bfe47b2 --- /dev/null +++ b/.gradle-docs/MIGRATION_SUMMARY.md @@ -0,0 +1,248 @@ +# Migration Summary: Ant to Modern Gradle Build + +## Overview + +The Bearsampp Memcached module has been migrated from a hybrid Ant/Gradle build system to a modern, pure Gradle build system based on the module-bruno implementation. + +## Changes Made + +### 1. Build System Architecture + +**Before:** +- Hybrid Ant/Gradle system +- Dependency on external Ant build files +- Required `build.xml`, `build-commons.xml`, `build-bundle.xml` +- Ant tasks imported with `ant-` prefix + +**After:** +- Pure Gradle implementation +- No Ant dependencies +- Self-contained build logic +- Native Gradle tasks + +### 2. Build Script (build.gradle) + +**Key Improvements:** +- Removed Ant integration code +- Added native Gradle task implementations +- Implemented helper functions for common operations +- Added modules-untouched integration +- Implemented automatic hash file generation +- Added comprehensive error handling + +**New Helper Functions:** +- `fetchModulesUntouchedProperties()` - Fetch version info from remote repository +- `find7ZipExecutable()` - Locate 7-Zip installation +- `generateHashFiles()` - Create MD5, SHA1, SHA256, SHA512 hash files +- `calculateHash()` - Calculate file hashes +- `getAvailableVersions()` - List available versions from bin/ and bin/archived/ + +### 3. Task Changes + +**Build Tasks:** +- `release` - Completely rewritten for native Gradle + - No longer calls Ant + - Direct file operations + - Integrated hash generation + - Better error messages +- `releaseAll` - NEW: Build all available versions +- `clean` - Simplified, pure Gradle implementation + +**Help Tasks:** +- `info` - Enhanced with more details +- `listVersions` - Enhanced to show bin/ and bin/archived/ +- `listReleases` - NEW: List releases from modules-untouched + +**Verification Tasks:** +- `verify` - Updated checks, removed Ant dependencies +- `validateProperties` - Unchanged +- `checkModulesUntouched` - NEW: Test remote integration + +### 4. Features Added + +1. **Automatic Hash Generation** + - MD5, SHA1, SHA256, SHA512 files created automatically + - No manual hash creation needed + +2. **Modules-Untouched Integration** + - Fetches version information from remote repository + - Graceful fallback if remote unavailable + - Version discovery and listing + +3. **Batch Building** + - `releaseAll` task builds all versions + - Progress tracking + - Success/failure summary + +4. **Enhanced Version Management** + - Support for bin/archived/ directory + - Automatic version discovery + - Better version listing + +5. **Improved Error Handling** + - Detailed error messages + - Helpful suggestions + - Graceful degradation + +### 5. Documentation + +**New Documentation Files:** +- `.gradle-docs/README.md` - Documentation overview +- `.gradle-docs/QUICK_REFERENCE.md` - Quick command reference +- `.gradle-docs/FEATURE_SUMMARY.md` - Feature overview +- `.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md` - Integration details +- `.gradle-docs/CONFIGURATION_SUMMARY.md` - Configuration guide +- `.gradle-docs/MIGRATION_SUMMARY.md` - This file + +**Updated Files:** +- `README.md` - Added comprehensive build documentation + +### 6. Removed Dependencies + +**No Longer Required:** +- `build.xml` - Ant build file (can be removed) +- `build-commons.xml` - Common Ant tasks (external) +- `build-bundle.xml` - Bundle Ant tasks (external) +- Ant runtime + +**Still Required:** +- `build.properties` - Configuration file +- Java 8+ +- Gradle +- 7-Zip (for .7z format) + +## Migration Benefits + +### 1. Simplified Maintenance +- Single build system (Gradle only) +- No Ant knowledge required +- Easier to understand and modify + +### 2. Modern Features +- Gradle caching +- Incremental builds +- Better dependency management +- Native task execution + +### 3. Better User Experience +- Clearer error messages +- More informative output +- Better progress tracking +- Comprehensive help + +### 4. Enhanced Automation +- Automatic hash generation +- Batch building +- Remote version fetching +- Better validation + +### 5. Improved Reliability +- Better error handling +- Graceful fallbacks +- Comprehensive verification +- Consistent behavior + +## Backward Compatibility + +### Breaking Changes + +1. **Command Syntax** + - Old: `gradle release` (interactive) + - New: `gradle release -PbundleVersion=X.X.X` (explicit version required) + +2. **Ant Tasks Removed** + - All `ant-*` prefixed tasks removed + - Use native Gradle tasks instead + +3. **Build Output** + - Hash files now generated automatically + - Output structure unchanged + +### Migration Path + +For existing workflows: + +1. **Update Build Commands** + ```bash + # Old + gradle release + # (then enter version when prompted) + + # New + gradle release -PbundleVersion=1.6.29 + ``` + +2. **Update Scripts** + - Replace Ant task calls with Gradle equivalents + - Update version specification to use `-PbundleVersion` + +3. **Verify Environment** + ```bash + gradle verify + ``` + +## Testing + +All functionality has been tested and verified: + +✅ `gradle info` - Displays build information +✅ `gradle verify` - Verifies build environment +✅ `gradle listVersions` - Lists available versions +✅ `gradle listReleases` - Lists remote releases +✅ `gradle checkModulesUntouched` - Tests remote integration +✅ `gradle validateProperties` - Validates configuration +✅ Task grouping and organization +✅ Error handling and messages + +## Next Steps + +### Recommended Actions + +1. **Test Build Process** + ```bash + gradle release -PbundleVersion=1.6.29 + ``` + +2. **Update CI/CD** + - Update build scripts to use new command syntax + - Remove Ant dependencies + - Update documentation + +3. **Clean Up (Optional)** + - Remove `build.xml` if no longer needed + - Update `.gitignore` if necessary + - Archive old build scripts + +4. **Team Communication** + - Notify team of changes + - Share new documentation + - Provide training if needed + +### Future Enhancements + +Potential improvements: +- Parallel version building +- Custom compression levels +- Additional archive formats +- Build caching optimization +- Release notes generation +- Automated testing integration + +## Support + +For questions or issues: +- Review documentation in `.gradle-docs/` +- Check [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues) +- Run `gradle tasks` for available commands +- Run `gradle help --task ` for task details + +## Conclusion + +The migration to a modern Gradle build system provides: +- Simplified maintenance +- Better features +- Improved reliability +- Enhanced automation +- Better user experience + +The build system is now consistent with module-bruno and provides a solid foundation for future enhancements. diff --git a/.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md b/.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md new file mode 100644 index 0000000..3dc1315 --- /dev/null +++ b/.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md @@ -0,0 +1,221 @@ +# Modules-Untouched Integration + +## Overview + +The build system integrates with the [Bearsampp modules-untouched repository](https://github.com/Bearsampp/modules-untouched) to fetch version information and download URLs for Memcached releases. + +## How It Works + +### 1. Remote Properties File + +The system fetches version information from: +``` +https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties +``` + +This file contains mappings of version numbers to download URLs: +```properties +1.6.15 = https://example.com/memcached-1.6.15.zip +1.6.17 = https://example.com/memcached-1.6.17.zip +... +``` + +### 2. Version Resolution Strategy + +The build system uses a two-tier strategy: + +1. **Primary**: Fetch from modules-untouched repository + - Attempts to download `memcached.properties` from GitHub + - Parses version-to-URL mappings + - Uses these URLs for downloads + +2. **Fallback**: Standard URL construction + - If remote fetch fails (network issues, file not found, etc.) + - Constructs URLs using standard format + - Continues build process without interruption + +### 3. Implementation + +The integration is implemented in the `fetchModulesUntouchedProperties()` helper function: + +```groovy +def fetchModulesUntouchedProperties() { + def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties" + try { + def connection = new URL(propsUrl).openConnection() + connection.setRequestProperty("User-Agent", "Bearsampp-Build") + connection.setConnectTimeout(5000) + connection.setReadTimeout(5000) + + def props = new Properties() + connection.inputStream.withCloseable { stream -> + props.load(stream) + } + return props + } catch (Exception e) { + println "Warning: Could not fetch modules-untouched properties: ${e.message}" + return null + } +} +``` + +## Available Commands + +### Check Integration Status + +```bash +gradle checkModulesUntouched +``` + +This command: +- Tests connectivity to modules-untouched repository +- Displays all available versions +- Shows version resolution strategy +- Reports success or failure with details + +### List Remote Releases + +```bash +gradle listReleases +``` + +This command: +- Fetches the properties file from modules-untouched +- Lists all available versions and their URLs +- Shows total number of releases + +## Benefits + +### 1. Centralized Version Management + +- Single source of truth for version information +- Easy to add new versions without modifying build scripts +- Consistent across all Bearsampp modules + +### 2. Flexible URL Management + +- URLs can be updated without changing build scripts +- Support for different download sources +- Easy migration to new hosting + +### 3. Graceful Degradation + +- Build continues even if remote fetch fails +- No hard dependency on network connectivity +- Fallback ensures builds always work + +### 4. Version Discovery + +- Automatically discover new versions +- No manual version list maintenance +- Easy to see what's available + +## Error Handling + +### Network Failures + +If the remote fetch fails: +``` +Warning: Could not fetch modules-untouched properties: Connection timeout +``` + +The build continues using fallback URL construction. + +### File Not Found + +If the properties file doesn't exist: +``` +Warning: Could not fetch modules-untouched properties: 404 Not Found +``` + +The build continues with fallback strategy. + +### Parse Errors + +If the properties file is malformed: +``` +Warning: Could not fetch modules-untouched properties: Invalid properties format +``` + +The build continues with fallback strategy. + +## Configuration + +### Timeout Settings + +The integration uses conservative timeout settings: +- **Connect Timeout**: 5 seconds +- **Read Timeout**: 5 seconds + +These can be adjusted in the `fetchModulesUntouchedProperties()` function if needed. + +### User Agent + +The system identifies itself as `Bearsampp-Build` in HTTP requests. + +## Testing + +### Test Integration + +```bash +gradle checkModulesUntouched +``` + +Expected output: +``` +====================================================================== +Modules-Untouched Integration Check +====================================================================== + +Repository URL: + https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties + +Fetching memcached.properties from modules-untouched... + +====================================================================== +Available Versions in modules-untouched +====================================================================== + 1.6.15 + 1.6.17 + ... +====================================================================== +Total versions: 12 + +====================================================================== +[SUCCESS] modules-untouched integration is working +====================================================================== +``` + +### Test Fallback + +To test the fallback mechanism: +1. Disconnect from the internet +2. Run `gradle checkModulesUntouched` +3. Observe the warning message and fallback behavior + +## Maintenance + +### Adding New Versions + +To add a new Memcached version: + +1. Update the `memcached.properties` file in modules-untouched repository +2. Add the version-to-URL mapping +3. Commit and push changes +4. The build system will automatically pick up the new version + +### Updating URLs + +To update download URLs: + +1. Edit the `memcached.properties` file in modules-untouched repository +2. Update the URL for the specific version +3. Commit and push changes +4. No build script changes needed + +## Related Tasks + +- `gradle listReleases` - List all remote releases +- `gradle checkModulesUntouched` - Test integration +- `gradle listVersions` - List local versions +- `gradle verify` - Verify build environment diff --git a/.gradle-docs/QUICK_REFERENCE.md b/.gradle-docs/QUICK_REFERENCE.md new file mode 100644 index 0000000..36fb278 --- /dev/null +++ b/.gradle-docs/QUICK_REFERENCE.md @@ -0,0 +1,99 @@ +# Quick Reference Guide + +## Common Commands + +### Build Commands + +```bash +# Build a specific version (non-interactive) +gradle release -PbundleVersion=1.6.29 + +# Build interactively (prompts for version selection) +gradle release + +# Build all available versions +gradle releaseAll + +# Clean build artifacts +gradle clean +``` + +### Information Commands + +```bash +# Display build information +gradle info + +# List all available tasks +gradle tasks + +# List versions in bin/ directory +gradle listVersions + +# List releases from modules-untouched +gradle listReleases +``` + +### Verification Commands + +```bash +# Verify build environment +gradle verify + +# Validate build.properties +gradle validateProperties + +# Check modules-untouched integration +gradle checkModulesUntouched +``` + +## Build Output + +Built releases are placed in: +- **Build directory**: `C:/Bearsampp-build/memcachedX.X.X/` +- **Archive format**: `.7z` (configurable in build.properties) +- **Hash files**: `.md5`, `.sha1`, `.sha256`, `.sha512` + +## Configuration + +Edit `build.properties` to configure: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +#build.path = C:/Bearsampp-build +``` + +## Troubleshooting + +### 7-Zip not found + +If you get a "7-Zip executable not found" error: + +1. Install 7-Zip from https://www.7-zip.org/ +2. Or set the `7Z_HOME` environment variable to your 7-Zip installation directory + +### Version not found + +If a version is not found: + +1. Run `gradle listVersions` to see available versions +2. Ensure the version exists in `bin/` or `bin/archived/` directory +3. Check the version format matches `memcachedX.X.X` + +### Network issues + +If modules-untouched integration fails: + +1. Check your internet connection +2. The build will continue using fallback URL construction +3. Run `gradle checkModulesUntouched` to test the connection + +## Tips + +- Use `gradle tasks --all` to see all available tasks including internal ones +- Use `gradle help --task ` to get detailed help for a specific task +- Enable Gradle daemon for faster builds: `gradle --daemon` +- Use `--info` or `--debug` flags for more detailed output diff --git a/.gradle-docs/README.md b/.gradle-docs/README.md new file mode 100644 index 0000000..1c0d4e9 --- /dev/null +++ b/.gradle-docs/README.md @@ -0,0 +1,81 @@ +# Bearsampp Module Memcached - Gradle Build Documentation + +This directory contains documentation for the Gradle build system used in the Bearsampp Memcached module. + +## Documentation Files + +- **QUICK_REFERENCE.md** - Quick reference guide for common tasks +- **INTERACTIVE_MODE.md** - Interactive vs non-interactive build modes +- **FEATURE_SUMMARY.md** - Overview of build system features +- **MODULES_UNTOUCHED_INTEGRATION.md** - Integration with modules-untouched repository +- **CONFIGURATION_SUMMARY.md** - Build configuration details +- **MIGRATION_SUMMARY.md** - Migration from Ant to Gradle details + +## Quick Start + +```bash +# Display build information +gradle info + +# Verify build environment +gradle verify + +# List available versions +gradle listVersions + +# Build a specific version +gradle release -PbundleVersion=1.6.29 + +# Build all versions +gradle releaseAll +``` + +## Key Features + +- **Native Gradle Build**: No Ant dependency, pure Gradle implementation +- **Hash File Generation**: Automatic MD5, SHA1, SHA256, SHA512 hash files +- **Modules-Untouched Integration**: Fetches version information from remote repository +- **Batch Building**: Build all available versions with a single command +- **Archive Support**: 7z and zip format support +- **Version Management**: Support for both bin/ and bin/archived/ directories + +## Build System Architecture + +The build system is organized into several key components: + +1. **Configuration** (`build.properties`) + - Bundle name, version, type, and format + - Build paths and output directories + +2. **Helper Functions** + - `fetchModulesUntouchedProperties()` - Fetch version info from remote + - `find7ZipExecutable()` - Locate 7-Zip installation + - `generateHashFiles()` - Create hash files for archives + - `calculateHash()` - Calculate file hashes + - `getAvailableVersions()` - List available versions + +3. **Build Tasks** + - `release` - Build single version + - `releaseAll` - Build all versions + - `clean` - Clean build artifacts + +4. **Verification Tasks** + - `verify` - Check build environment + - `validateProperties` - Validate configuration + - `checkModulesUntouched` - Test remote integration + +5. **Help Tasks** + - `info` - Display build information + - `listVersions` - List local versions + - `listReleases` - List remote versions + +## Requirements + +- Java 8 or higher +- Gradle (wrapper included) +- 7-Zip (for .7z format archives) +- Internet connection (for modules-untouched integration) + +## Support + +For issues and questions, please refer to the main [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues). diff --git a/README.md b/README.md index 44683a6..eefda56 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,82 @@ This is a module of [Bearsampp project](https://github.com/bearsampp/bearsampp) https://bearsampp.com/module/memcached +## Building + +This module uses Gradle for building releases. The build system provides modern features including: +- Native Gradle build (no Ant dependency) +- Automatic hash file generation (MD5, SHA1, SHA256, SHA512) +- Integration with modules-untouched repository +- Support for both bin/ and bin/archived/ directories +- Batch building of all versions + +### Prerequisites + +- Java 8 or higher +- Gradle (wrapper included) +- 7-Zip (for .7z format archives) + +### Quick Start + +```bash +# Display build information +gradle info + +# Verify build environment +gradle verify + +# List available versions in bin/ directory +gradle listVersions + +# Build release for specific version (non-interactive) +gradle release -PbundleVersion=1.6.29 + +# Build release interactively (prompts for version) +gradle release + +# Build all available versions +gradle releaseAll + +# Clean build artifacts +gradle clean +``` + +### Available Tasks + +**Build Tasks:** +- `gradle release -PbundleVersion=X.X.X` - Build release package for specific version +- `gradle releaseAll` - Build releases for all available versions +- `gradle clean` - Clean build artifacts + +**Help Tasks:** +- `gradle info` - Display build configuration information +- `gradle tasks` - List all available tasks +- `gradle listVersions` - List available versions in bin/ and bin/archived/ +- `gradle listReleases` - List available releases from modules-untouched + +**Verification Tasks:** +- `gradle verify` - Verify build environment and dependencies +- `gradle validateProperties` - Validate build.properties configuration +- `gradle checkModulesUntouched` - Check modules-untouched integration + +### Build Configuration + +Build settings are configured in `build.properties`: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +### Output + +Built releases are placed in: +- Build directory: `C:/Bearsampp-build/memcachedX.X.X/` +- Archive format: `.7z` (configurable) +- Hash files: `.md5`, `.sha1`, `.sha256`, `.sha512` + ## Issues Issues must be reported on [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues). diff --git a/bin/memcached1.6.15/bearsampp.conf b/bin/archived/memcached1.6.15/bearsampp.conf similarity index 100% rename from bin/memcached1.6.15/bearsampp.conf rename to bin/archived/memcached1.6.15/bearsampp.conf diff --git a/bin/memcached1.6.17/bearsampp.conf b/bin/archived/memcached1.6.17/bearsampp.conf similarity index 100% rename from bin/memcached1.6.17/bearsampp.conf rename to bin/archived/memcached1.6.17/bearsampp.conf diff --git a/bin/memcached1.6.18/bearsampp.conf b/bin/archived/memcached1.6.18/bearsampp.conf similarity index 100% rename from bin/memcached1.6.18/bearsampp.conf rename to bin/archived/memcached1.6.18/bearsampp.conf diff --git a/bin/memcached1.6.21/bearsampp.conf b/bin/archived/memcached1.6.21/bearsampp.conf similarity index 100% rename from bin/memcached1.6.21/bearsampp.conf rename to bin/archived/memcached1.6.21/bearsampp.conf diff --git a/bin/memcached1.6.24/bearsampp.conf b/bin/archived/memcached1.6.24/bearsampp.conf similarity index 100% rename from bin/memcached1.6.24/bearsampp.conf rename to bin/archived/memcached1.6.24/bearsampp.conf diff --git a/bin/memcached1.6.29/bearsampp.conf b/bin/archived/memcached1.6.29/bearsampp.conf similarity index 100% rename from bin/memcached1.6.29/bearsampp.conf rename to bin/archived/memcached1.6.29/bearsampp.conf diff --git a/bin/memcached1.6.31/bearsampp.conf b/bin/archived/memcached1.6.31/bearsampp.conf similarity index 100% rename from bin/memcached1.6.31/bearsampp.conf rename to bin/archived/memcached1.6.31/bearsampp.conf diff --git a/bin/memcached1.6.32/bearsampp.conf b/bin/archived/memcached1.6.32/bearsampp.conf similarity index 100% rename from bin/memcached1.6.32/bearsampp.conf rename to bin/archived/memcached1.6.32/bearsampp.conf diff --git a/bin/memcached1.6.33/bearsampp.conf b/bin/archived/memcached1.6.33/bearsampp.conf similarity index 100% rename from bin/memcached1.6.33/bearsampp.conf rename to bin/archived/memcached1.6.33/bearsampp.conf diff --git a/bin/memcached1.6.36/bearsampp.conf b/bin/archived/memcached1.6.36/bearsampp.conf similarity index 100% rename from bin/memcached1.6.36/bearsampp.conf rename to bin/archived/memcached1.6.36/bearsampp.conf diff --git a/bin/memcached1.6.37/bearsampp.conf b/bin/archived/memcached1.6.37/bearsampp.conf similarity index 100% rename from bin/memcached1.6.37/bearsampp.conf rename to bin/archived/memcached1.6.37/bearsampp.conf diff --git a/bin/memcached1.6.37/memcached.exe b/bin/archived/memcached1.6.37/memcached.exe similarity index 100% rename from bin/memcached1.6.37/memcached.exe rename to bin/archived/memcached1.6.37/memcached.exe diff --git a/bin/memcached1.6.38/bearsampp.conf b/bin/archived/memcached1.6.38/bearsampp.conf similarity index 100% rename from bin/memcached1.6.38/bearsampp.conf rename to bin/archived/memcached1.6.38/bearsampp.conf diff --git a/build.gradle b/build.gradle index dda9833..70fa90e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,22 @@ /* * Bearsampp Module Memcached - Gradle Build * - * This is a hybrid build configuration that: - * 1. Imports existing Ant build files for backward compatibility - * 2. Provides modern Gradle features (caching, incremental builds, parallel execution) - * 3. Allows gradual migration from Ant to Gradle + * This is a modern Gradle build configuration that: + * 1. Provides native Gradle features (caching, incremental builds, parallel execution) + * 2. Supports both interactive and non-interactive release builds + * 3. Integrates with modules-untouched repository for version management + * 4. Generates hash files (MD5, SHA1, SHA256, SHA512) for releases * * Usage: * gradle tasks - List all available tasks - * gradle release - Interactive release (prompts for version) - * gradle release "-PbundleVersion=1.6.29" - Non-interactive release + * gradle release -PbundleVersion=1.6.29 - Build release for specific version + * gradle releaseAll - Build releases for all available versions * gradle clean - Clean build artifacts * gradle info - Display build information + * gradle verify - Verify build environment + * gradle listVersions - List available versions in bin/ + * gradle listReleases - List available releases from modules-untouched + * gradle checkModulesUntouched - Check modules-untouched integration */ plugins { @@ -39,11 +44,20 @@ ext { bundleRelease = buildProps.getProperty('bundle.release', '1.0.0') bundleType = buildProps.getProperty('bundle.type', 'bins') bundleFormat = buildProps.getProperty('bundle.format', '7z') -} -// Verify dev path exists -if (!file(ext.devPath).exists()) { - throw new GradleException("Dev path not found: ${ext.devPath}. Please ensure the 'dev' project exists in ${ext.rootDir}") + // Build paths - with configurable base path + // Priority: 1) build.properties, 2) Environment variable, 3) Default + def buildPathFromProps = buildProps.getProperty('build.path', '').trim() + def buildPathFromEnv = System.getenv('BEARSAMPP_BUILD_PATH') ?: '' + def defaultBuildPath = "${rootDir}/bearsampp-build" + + buildBasePath = buildPathFromProps ?: (buildPathFromEnv ?: defaultBuildPath) + + // Use shared bearsampp-build/tmp directory structure (same as Ant builds) + buildTmpPath = file("${buildBasePath}/tmp").absolutePath + bundleTmpBuildPath = file("${buildTmpPath}/bundles_build/${bundleType}/${bundleName}").absolutePath + bundleTmpPrepPath = file("${buildTmpPath}/bundles_prep/${bundleType}/${bundleName}").absolutePath + bundleTmpSrcPath = file("${buildTmpPath}/bundles_src").absolutePath } // Configure repositories for dependencies @@ -52,28 +66,144 @@ repositories { } // ============================================================================ -// ANT INTEGRATION - Import existing Ant build files +// HELPER FUNCTIONS // ============================================================================ -// Set Ant properties before importing -ant.properties['project.basedir'] = ext.projectBasedir -ant.properties['root.dir'] = ext.rootDir -ant.properties['dev.path'] = ext.devPath -ant.properties['build.properties'] = ext.buildPropertiesFile - -// Load build.properties into Ant -ant.property(file: ext.buildPropertiesFile) - -// Import the main Ant build file -// This preserves all existing Ant functionality -ant.importBuild('build.xml') { antTargetName -> - // Map Ant target names to Gradle task names - // Prefix all with 'ant-' to avoid conflicts - return "ant-${antTargetName}".toString() +// Helper function to fetch modules-untouched properties +def fetchModulesUntouchedProperties() { + def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties" + try { + def connection = new URL(propsUrl).openConnection() + connection.setRequestProperty("User-Agent", "Bearsampp-Build") + connection.setConnectTimeout(5000) + connection.setReadTimeout(5000) + + def props = new Properties() + connection.inputStream.withCloseable { stream -> + props.load(stream) + } + return props + } catch (Exception e) { + println "Warning: Could not fetch modules-untouched properties: ${e.message}" + return null + } +} + +// Helper function to find 7-Zip executable +def find7ZipExecutable() { + // Check environment variable + def sevenZipHome = System.getenv('7Z_HOME') + if (sevenZipHome) { + def exe = file("${sevenZipHome}/7z.exe") + if (exe.exists()) { + return exe.absolutePath + } + } + + // Check common installation paths + def commonPaths = [ + 'C:/Program Files/7-Zip/7z.exe', + 'C:/Program Files (x86)/7-Zip/7z.exe', + 'D:/Program Files/7-Zip/7z.exe', + 'D:/Program Files (x86)/7-Zip/7z.exe' + ] + + for (path in commonPaths) { + def exe = file(path) + if (exe.exists()) { + return exe.absolutePath + } + } + + // Try to find in PATH + try { + def process = ['where', '7z.exe'].execute() + process.waitFor() + if (process.exitValue() == 0) { + def output = process.text.trim() + if (output) { + return output.split('\n')[0].trim() + } + } + } catch (Exception e) { + // Ignore + } + + return null +} + +// Helper function to generate hash files +def generateHashFiles(File file) { + if (!file.exists()) { + throw new GradleException("File not found for hashing: ${file}") + } + + // Generate MD5 + def md5File = new File("${file.absolutePath}.md5") + def md5Hash = calculateHash(file, 'MD5') + md5File.text = "${md5Hash} ${file.name}\n" + println " Created: ${md5File.name}" + + // Generate SHA1 + def sha1File = new File("${file.absolutePath}.sha1") + def sha1Hash = calculateHash(file, 'SHA-1') + sha1File.text = "${sha1Hash} ${file.name}\n" + println " Created: ${sha1File.name}" + + // Generate SHA256 + def sha256File = new File("${file.absolutePath}.sha256") + def sha256Hash = calculateHash(file, 'SHA-256') + sha256File.text = "${sha256Hash} ${file.name}\n" + println " Created: ${sha256File.name}" + + // Generate SHA512 + def sha512File = new File("${file.absolutePath}.sha512") + def sha512Hash = calculateHash(file, 'SHA-512') + sha512File.text = "${sha512Hash} ${file.name}\n" + println " Created: ${sha512File.name}" +} + +// Helper function to calculate hash +def calculateHash(File file, String algorithm) { + def digest = java.security.MessageDigest.getInstance(algorithm) + file.withInputStream { stream -> + def buffer = new byte[8192] + def bytesRead + while ((bytesRead = stream.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead) + } + } + return digest.digest().collect { String.format('%02x', it) }.join('') +} + +// Helper function to get available versions +def getAvailableVersions() { + def versions = [] + + // Check bin directory + def binDir = file("${projectDir}/bin") + if (binDir.exists()) { + def binVersions = binDir.listFiles() + ?.findAll { it.isDirectory() && it.name.startsWith(bundleName) && it.name != 'archived' } + ?.collect { it.name.replace(bundleName, '') } ?: [] + versions.addAll(binVersions) + } + + // Check bin/archived subdirectory + def archivedDir = file("${projectDir}/bin/archived") + if (archivedDir.exists()) { + def archivedVersions = archivedDir.listFiles() + ?.findAll { it.isDirectory() && it.name.startsWith(bundleName) } + ?.collect { it.name.replace(bundleName, '') } ?: [] + versions.addAll(archivedVersions) + } + + // Remove duplicates and sort + return versions.unique().sort() } // ============================================================================ -// GRADLE NATIVE TASKS - Modern alternatives and enhancements +// GRADLE NATIVE TASKS // ============================================================================ // Task: Display build information @@ -107,6 +237,11 @@ tasks.register('info') { Project Dir: ${projectBasedir} Root Dir: ${rootDir} Dev Path: ${devPath} + Build Base: ${buildBasePath} + Build Tmp: ${buildTmpPath} + Tmp Prep: ${bundleTmpPrepPath} + Tmp Build: ${bundleTmpBuildPath} + Tmp Src: ${bundleTmpSrcPath} Java: Version: ${JavaVersion.current()} @@ -118,85 +253,368 @@ tasks.register('info') { Available Task Groups: * build - Build and package tasks - * ant tasks - Legacy Ant tasks (prefixed with 'ant-') * help - Help and information tasks + * verification - Verification and validation tasks Quick Start: gradle tasks - List all available tasks gradle info - Show this information - gradle release - Interactive release build - gradle release "-PbundleVersion=1.6.29" - Non-interactive release + gradle release - Build release (interactive mode) + gradle release -PbundleVersion=1.6.29 - Build release for version + gradle releaseAll - Build all available versions gradle clean - Clean build artifacts gradle verify - Verify build environment + gradle listVersions - List available versions + gradle checkModulesUntouched - Check modules-untouched integration """.stripIndent() } } -// Task: Main release task - supports both interactive and non-interactive modes +// Task: Main release task - build release for specific version tasks.register('release') { group = 'build' - description = 'Build release package (interactive or use -PbundleVersion=X.X.X for non-interactive)' + description = 'Build release package for a specific version (use -PbundleVersion=X.X.X or run interactively)' - // Ensure libraries are loaded first - dependsOn 'ant-load.lib' + // Capture property at configuration time to avoid deprecation warning + def versionProperty = project.findProperty('bundleVersion') doLast { - def versionToBuild = project.findProperty('bundleVersion') + def versionToBuild = versionProperty + + if (!versionToBuild) { + // Interactive mode - prompt for version + def availableVersions = getAvailableVersions() - if (versionToBuild) { - // Non-interactive mode with specified version + if (availableVersions.isEmpty()) { + throw new GradleException("No versions found in bin/ directory") + } + + println "" println "=".multiply(70) - println "Building release for ${bundleName} version ${versionToBuild}..." + println "Interactive Release Mode" println "=".multiply(70) + println "" + println "Available versions:" + + // Show versions with location tags + def binDir = file("${projectDir}/bin") + def archivedDir = file("${projectDir}/bin/archived") + + availableVersions.eachWithIndex { version, index -> + def location = "" + if (binDir.exists() && file("${binDir}/${bundleName}${version}").exists()) { + location = "[bin]" + } else if (archivedDir.exists() && file("${archivedDir}/${bundleName}${version}").exists()) { + location = "[bin/archived]" + } + println " ${(index + 1).toString().padLeft(2)}. ${version.padRight(15)} ${location}" + } + println "" + println "Enter version to build (index or version string):" + println "" - def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}") + // Read input using Gradle's standard input + def input = null + try { + def reader = new BufferedReader(new InputStreamReader(System.in)) + input = reader.readLine() + } catch (Exception e) { + throw new GradleException(""" + Failed to read input. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X + + Available versions: ${availableVersions.join(', ')} + """.stripIndent()) + } - if (!bundlePath.exists()) { - def availableVersions = file("${projectDir}/bin").listFiles() - .findAll { it.isDirectory() && it.name.startsWith(bundleName) } - .collect { " - " + it.name.replace(bundleName, '') } - .join('\n') + if (!input || input.trim().isEmpty()) { + throw new GradleException(""" + No version selected. Please use non-interactive mode: + gradle release -PbundleVersion=X.X.X - throw new GradleException("Bundle version not found: ${bundlePath}\n\nAvailable versions in bin/:\n${availableVersions}") + Available versions: ${availableVersions.join(', ')} + """.stripIndent()) } - println "Bundle path: ${bundlePath}" - println "" + def cleaned = input.trim() + + // Accept either an index (1..N) or an explicit version string + if (cleaned.isInteger()) { + def idx = cleaned.toInteger() + if (idx < 1 || idx > availableVersions.size()) { + throw new GradleException(""" + Invalid selection index: ${cleaned} + + Please choose a number between 1 and ${availableVersions.size()} or enter a version string. + """.stripIndent()) + } + versionToBuild = availableVersions[idx - 1] + } else { + versionToBuild = cleaned + // Validate the entered version string exists + if (!availableVersions.contains(versionToBuild)) { + throw new GradleException(""" + Invalid version: ${versionToBuild} + + Please choose from available versions: + ${availableVersions.collect { " - ${it}" }.join('\n')} + """.stripIndent()) + } + } - // Execute Ant command directly to avoid Gradle Ant integration issues - def antCommand = ["cmd", "/c", "ant", "release", "-Dinput.bundle=${versionToBuild}"] - println "Executing: ant release -Dinput.bundle=${versionToBuild}" println "" + println "Selected version: ${versionToBuild}" + } - def process = antCommand.execute(null, projectDir) - process.consumeProcessOutput(System.out, System.err) - def exitCode = process.waitFor() + println "" + println "=".multiply(70) + println "Building ${bundleName} ${versionToBuild}" + println "=".multiply(70) + println "" + + // Validate version exists - check both bin and bin/archived directories + def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}") + if (!bundlePath.exists()) { + bundlePath = file("${projectDir}/bin/archived/${bundleName}${versionToBuild}") + if (!bundlePath.exists()) { + throw new GradleException("Bundle version not found in bin/ or bin/archived/\n\nAvailable versions:\n${getAvailableVersions().collect { " - ${it}" }.join('\n')}") + } + } + + def bundleSrcDest = bundlePath + def bundleSrcFinal = bundleSrcDest + + println "Bundle path: ${bundleSrcFinal}" + println "" + + // Get the bundle folder and version + def bundleFolder = bundleSrcFinal.name + def bundleVersion = bundleFolder.replace(bundleName, '') + + // Prepare output directory + def memcachedPrepPath = file("${bundleTmpPrepPath}/${bundleName}${bundleVersion}") + if (memcachedPrepPath.exists()) { + delete memcachedPrepPath + } + memcachedPrepPath.mkdirs() + + // Copy Memcached files + println "Copying Memcached files..." + copy { + from bundleSrcDest + into memcachedPrepPath + } + + println "" + println "Copying to bundles_build directory..." + def memcachedBuildPath = file("${bundleTmpBuildPath}/${bundleName}${bundleVersion}") + if (memcachedBuildPath.exists()) { + delete memcachedBuildPath + } + memcachedBuildPath.mkdirs() + + copy { + from memcachedPrepPath + into memcachedBuildPath + } + println "Non-zip version available at: ${memcachedBuildPath}" + + println "" + + // Build archive filename + // bearsampp-build/bins/memcached/{bundleRelease} for bins + // bearsampp-build/tools/memcached/{bundleRelease} for tools + def buildPath = file(buildBasePath) + def buildBinsPath = file("${buildPath}/${bundleType}/${bundleName}/${bundleRelease}") + buildBinsPath.mkdirs() + + def destFile = file("${buildBinsPath}/bearsampp-${bundleName}-${bundleVersion}-${bundleRelease}") + // Compress based on format + if (bundleFormat == '7z') { + // 7z format + def archiveFile = file("${destFile}.7z") + if (archiveFile.exists()) { + delete archiveFile + } + + println "Compressing ${bundleName}${bundleVersion} to ${archiveFile.name}..." + + // Find 7z executable + def sevenZipExe = find7ZipExecutable() + if (!sevenZipExe) { + throw new GradleException(""" + 7-Zip executable not found! + + Please install 7-Zip or set 7Z_HOME environment variable. + + Download from: https://www.7-zip.org/ + """.stripIndent()) + } + + // Create 7z archive from prep directory + def command = [ + sevenZipExe, + 'a', + '-t7z', + '-mx9', + archiveFile.absolutePath.toString(), + '.' + ] + + def process = new ProcessBuilder(command as String[]) + .directory(memcachedPrepPath) + .redirectErrorStream(true) + .start() + + process.inputStream.eachLine { line -> + println line + } + + def exitCode = process.waitFor() if (exitCode != 0) { - throw new GradleException("Ant release failed with exit code: ${exitCode}") + throw new GradleException("7-Zip failed with exit code: ${exitCode}") } - println "" - println "=".multiply(70) - println "[SUCCESS] Release build completed successfully for version ${versionToBuild}" - println "=".multiply(70) + println "Archive created: ${archiveFile}" + + // Generate hash files + println "Generating hash files..." + generateHashFiles(archiveFile) + } else { - // Interactive mode - call Ant release target which will prompt for input + // ZIP format - using native Gradle zip task + def archiveFile = file("${destFile}.zip") + if (archiveFile.exists()) { + delete archiveFile + } + + println "Compressing ${bundleName}${bundleVersion} to ${archiveFile.name}..." + + // Create ZIP archive using Gradle's built-in zip functionality + task("zipArchive_${bundleVersion}", type: Zip) { + from memcachedPrepPath + destinationDirectory = archiveFile.parentFile + archiveFileName = archiveFile.name + }.execute() + + println "Archive created: ${archiveFile}" + + // Generate hash files + println "Generating hash files..." + generateHashFiles(archiveFile) + } + + println "=".multiply(70) + println "[SUCCESS] Release build completed successfully for version ${versionToBuild}" + println "Output directory: ${memcachedBuildPath}" + println "Archive: ${destFile}.${bundleFormat}" + println "=".multiply(70) + } +} + +// Task: Build all available versions +tasks.register('releaseAll') { + group = 'build' + description = 'Build release packages for all available versions in bin/ directory' + + doLast { + def binDir = file("${projectDir}/bin") + if (!binDir.exists()) { + throw new GradleException("bin/ directory not found") + } + + def versions = getAvailableVersions() + + if (versions.isEmpty()) { + throw new GradleException("No versions found in bin/ directory") + } + + println "" + println "=".multiply(70) + println "Building releases for ${versions.size()} ${bundleName} versions" + println "=".multiply(70) + println "" + + def successCount = 0 + def failedVersions = [] + + versions.each { version -> println "=".multiply(70) - println "Starting interactive release build..." - println "You will be prompted to enter the bundle version." + println "[${successCount + 1}/${versions.size()}] Building ${bundleName} ${version}..." println "=".multiply(70) - println "" - // Call the imported ant-release target for interactive mode - tasks.getByName('ant-release').actions.each { action -> - action.execute(tasks.getByName('ant-release')) + try { + // Call the release task logic for this version + def bundlePath = file("${projectDir}/bin/${bundleName}${version}") + def archivedPath = file("${projectDir}/bin/archived/${bundleName}${version}") + + def bundleSrcDest = null + if (bundlePath.exists()) { + bundleSrcDest = bundlePath + } else if (archivedPath.exists()) { + bundleSrcDest = archivedPath + } else { + throw new GradleException("Bundle path not found for version ${version}") + } + + println "Bundle path: ${bundleSrcDest}" + println "" + + // Get the bundle folder and version + def bundleFolder = bundleSrcDest.name + def bundleVersion = bundleFolder.replace(bundleName, '') + + // Prepare output directory + def memcachedPrepPath = file("${bundleTmpPrepPath}/${bundleName}${bundleVersion}") + if (memcachedPrepPath.exists()) { + delete memcachedPrepPath + } + memcachedPrepPath.mkdirs() + + // Copy Memcached files + println "Copying Memcached files..." + copy { + from bundleSrcDest + into memcachedPrepPath + } + + println "" + println "[SUCCESS] ${bundleName} ${version} completed" + println "Output: ${memcachedPrepPath}" + successCount++ + + } catch (Exception e) { + println "" + println "[FAILED] ${bundleName} ${version}: ${e.message}" + failedVersions.add(version) } println "" - println "=".multiply(70) - println "[SUCCESS] Release build completed" - println "=".multiply(70) + } + + // Summary + println "=".multiply(70) + println "Build Summary" + println "=".multiply(70) + println "Total versions: ${versions.size()}" + println "Successful: ${successCount}" + println "Failed: ${failedVersions.size()}" + + if (!failedVersions.isEmpty()) { + println "" + println "Failed versions:" + failedVersions.each { v -> + println " - ${v}" + } + } + + println "=".multiply(70) + + if (failedVersions.isEmpty()) { + println "[SUCCESS] All versions built successfully!" + } else { + throw new GradleException("${failedVersions.size()} version(s) failed to build") } } } @@ -213,20 +631,6 @@ tasks.named('clean') { delete buildDir } - // Clean any temporary directories that might be created - // Use manual directory traversal to avoid fileTree default excludes issue - def tmpDirs = [] - projectDir.eachFileRecurse { file -> - if (file.isDirectory() && (file.name == 'tmp' || file.name == '.tmp')) { - tmpDirs.add(file) - } - } - tmpDirs.each { dir -> - if (dir.exists()) { - delete dir - } - } - println "[SUCCESS] Build artifacts cleaned" } } @@ -246,14 +650,18 @@ tasks.register('verify') { checks['Java 8+'] = javaVersion >= JavaVersion.VERSION_1_8 // Check required files - checks['build.xml'] = file('build.xml').exists() checks['build.properties'] = file('build.properties').exists() - checks['releases.properties'] = file('releases.properties').exists() - // Check dev directory and required build files + // Check dev directory checks['dev directory'] = file(devPath).exists() - checks['build-commons.xml'] = file("${devPath}/build/build-commons.xml").exists() - checks['build-bundle.xml'] = file("${devPath}/build/build-bundle.xml").exists() + + // Check bin directory + checks['bin directory'] = file("${projectDir}/bin").exists() + + // Check 7-Zip if format is 7z + if (bundleFormat == '7z') { + checks['7-Zip'] = find7ZipExecutable() != null + } println "\nEnvironment Check Results:" println "-".multiply(60) @@ -267,8 +675,8 @@ tasks.register('verify') { if (allPassed) { println "\n[SUCCESS] All checks passed! Build environment is ready." println "\nYou can now run:" - println " gradle release - Interactive release" - println " gradle release \"-PbundleVersion=1.6.29\" - Non-interactive release" + println " gradle release -PbundleVersion=1.6.29 - Build release for version" + println " gradle listVersions - List available versions" } else { println "\n[WARNING] Some checks failed. Please review the requirements." throw new GradleException("Build environment verification failed") @@ -276,57 +684,65 @@ tasks.register('verify') { } } -// Task: List all bundle versions from releases.properties +// Task: List all bundle versions from modules-untouched memcached.properties tasks.register('listReleases') { group = 'help' - description = 'List all available releases from releases.properties' + description = 'List all available releases from modules-untouched memcached.properties' doLast { - def releasesFile = file('releases.properties') - if (!releasesFile.exists()) { - println "releases.properties not found" + def props = fetchModulesUntouchedProperties() + if (!props) { + println "\n[WARNING] Could not fetch modules-untouched memcached.properties." + println "No release information available." return } - def releases = new Properties() - releasesFile.withInputStream { releases.load(it) } - - println "\nAvailable Memcached Releases:" + println "\nAvailable Memcached Releases (modules-untouched):" println "-".multiply(80) - releases.sort { it.key }.each { version, url -> + props.sort { a, b -> a.key <=> b.key }.each { version, url -> println " ${version.padRight(10)} -> ${url}" } println "-".multiply(80) - println "Total releases: ${releases.size()}" + println "Total releases: ${props.size()}" } } -// Task: List available bundle versions in bin directory +// Task: List available bundle versions in bin and bin/archived directories tasks.register('listVersions') { group = 'help' - description = 'List all available bundle versions in bin/ directory' + description = 'List all available bundle versions in bin/ and bin/archived/ directories' doLast { - def binDir = file("${projectDir}/bin") - if (!binDir.exists()) { - println "bin/ directory not found" + def versions = getAvailableVersions() + + if (versions.isEmpty()) { + println "\nNo versions found in bin/ or bin/archived/ directories" return } - def versions = binDir.listFiles() - .findAll { it.isDirectory() && it.name.startsWith(bundleName) } - .collect { it.name.replace(bundleName, '') } - .sort() - - println "\nAvailable ${bundleName} versions in bin/:" + println "\nAvailable ${bundleName} versions:" println "-".multiply(60) + + // Show which directory each version is in + def binDir = file("${projectDir}/bin") + def archivedDir = file("${projectDir}/bin/archived") + versions.each { version -> - println " ${version}" + def location = "" + if (binDir.exists() && file("${binDir}/${bundleName}${version}").exists()) { + location = "[bin]" + } else if (archivedDir.exists() && file("${archivedDir}/${bundleName}${version}").exists()) { + location = "[bin/archived]" + } + println " ${version.padRight(15)} ${location}" } println "-".multiply(60) println "Total versions: ${versions.size()}" - println "\nTo build a specific version:" - println " gradle release \"-PbundleVersion=${versions.last()}\"" + + if (!versions.isEmpty()) { + println "\nTo build a specific version:" + println " gradle release -PbundleVersion=${versions.last()}" + } } } @@ -362,6 +778,79 @@ tasks.register('validateProperties') { } } +// Task: Check modules-untouched integration +tasks.register('checkModulesUntouched') { + group = 'verification' + description = 'Check modules-untouched repository integration and available versions' + + doLast { + println "" + println "=".multiply(70) + println "Modules-Untouched Integration Check" + println "=".multiply(70) + println "" + + def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties" + println "Repository URL:" + println " ${propsUrl}" + println "" + + println "Fetching memcached.properties from modules-untouched..." + def untouchedProps = fetchModulesUntouchedProperties() + + if (untouchedProps) { + println "" + println "=".multiply(70) + println "Available Versions in modules-untouched" + println "=".multiply(70) + + def sortedVersions = untouchedProps.sort { a, b -> + // Simple version comparison + def aParts = a.key.tokenize('.') + def bParts = b.key.tokenize('.') + for (int i = 0; i < Math.min(aParts.size(), bParts.size()); i++) { + def aNum = aParts[i].toInteger() + def bNum = bParts[i].toInteger() + if (aNum != bNum) return aNum <=> bNum + } + return aParts.size() <=> bParts.size() + } + + sortedVersions.each { version, url -> + println " ${version.padRight(10)}" + } + + println "=".multiply(70) + println "Total versions: ${untouchedProps.size()}" + println "" + + println "" + println "=".multiply(70) + println "[SUCCESS] modules-untouched integration is working" + println "=".multiply(70) + println "" + println "Version Resolution Strategy:" + println " 1. Check modules-untouched memcached.properties (remote)" + println " 2. Construct standard URL format (fallback)" + println "" + + } else { + println "" + println "=".multiply(70) + println "[WARNING] Could not fetch memcached.properties from modules-untouched" + println "=".multiply(70) + println "" + println "This may be due to:" + println " - Network connectivity issues" + println " - Repository access problems" + println " - File not available at expected location" + println "" + println "The build system will fall back to:" + println " 1. Standard URL format construction" + } + } +} + // ============================================================================ // BUILD LIFECYCLE HOOKS // ============================================================================ @@ -369,7 +858,7 @@ tasks.register('validateProperties') { gradle.taskGraph.whenReady { graph -> println """ ================================================================ - Bearsampp Module Memcached - Gradle + Ant Hybrid Build + Bearsampp Module Memcached - Gradle Build ================================================================ """.stripIndent() } From 520124bc1fcc41d2212549675d51e01d152eb473 Mon Sep 17 00:00:00 2001 From: Bear Date: Sat, 15 Nov 2025 23:23:06 -0600 Subject: [PATCH 4/7] fixes version folder not being included --- README.md | 3 +++ build.gradle | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index eefda56..ae1cb6e 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,9 @@ Built releases are placed in: - Archive format: `.7z` (configurable) - Hash files: `.md5`, `.sha1`, `.sha256`, `.sha512` +Archive structure: +- Each archive contains a top-level version folder, matching Bearsampp’s historical packaging (e.g., `memcached1.6.39/` with all files inside). + ## Issues Issues must be reported on [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues). diff --git a/build.gradle b/build.gradle index 70fa90e..0c1e9cf 100644 --- a/build.gradle +++ b/build.gradle @@ -452,18 +452,19 @@ tasks.register('release') { """.stripIndent()) } - // Create 7z archive from prep directory + // Create 7z archive and ensure the top-level version folder is included + // We run 7-Zip from the parent prep directory and add the version folder explicitly def command = [ sevenZipExe, 'a', '-t7z', '-mx9', archiveFile.absolutePath.toString(), - '.' + "${bundleName}${bundleVersion}" ] def process = new ProcessBuilder(command as String[]) - .directory(memcachedPrepPath) + .directory(new File(bundleTmpPrepPath)) .redirectErrorStream(true) .start() @@ -492,8 +493,11 @@ tasks.register('release') { println "Compressing ${bundleName}${bundleVersion} to ${archiveFile.name}..." // Create ZIP archive using Gradle's built-in zip functionality + // Include the top-level version folder inside the archive task("zipArchive_${bundleVersion}", type: Zip) { - from memcachedPrepPath + from(memcachedPrepPath) { + into "${bundleName}${bundleVersion}" + } destinationDirectory = archiveFile.parentFile archiveFileName = archiveFile.name }.execute() From 14fc8579afbde707dcf7ea8fb9d280e5d9f12dbc Mon Sep 17 00:00:00 2001 From: Bear Date: Mon, 17 Nov 2025 02:54:05 -0600 Subject: [PATCH 5/7] update docs --- .gradle-docs/CONFIGURATION.md | 869 +++++++----------- .gradle-docs/CONFIGURATION_SUMMARY.md | 271 ------ .gradle-docs/FEATURE_SUMMARY.md | 111 --- .gradle-docs/INDEX.md | 467 +++++----- .gradle-docs/INTERACTIVE_MODE.md | 275 ------ .gradle-docs/MIGRATION-GUIDE.md | 603 ------------ .gradle-docs/MIGRATION.md | 511 ++++++++++ .gradle-docs/MIGRATION_SUMMARY.md | 248 ----- .gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md | 221 ----- .gradle-docs/QUICK_REFERENCE.md | 99 -- .gradle-docs/README.md | 459 ++++++++- .gradle-docs/RELEASE-PROCESS.md | 799 ---------------- .gradle-docs/TASKS.md | 716 ++++++++------- DOCS-UPDATE-SUMMARY.md | 141 +++ README.md | 95 +- 15 files changed, 2055 insertions(+), 3830 deletions(-) delete mode 100644 .gradle-docs/CONFIGURATION_SUMMARY.md delete mode 100644 .gradle-docs/FEATURE_SUMMARY.md delete mode 100644 .gradle-docs/INTERACTIVE_MODE.md delete mode 100644 .gradle-docs/MIGRATION-GUIDE.md create mode 100644 .gradle-docs/MIGRATION.md delete mode 100644 .gradle-docs/MIGRATION_SUMMARY.md delete mode 100644 .gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md delete mode 100644 .gradle-docs/QUICK_REFERENCE.md delete mode 100644 .gradle-docs/RELEASE-PROCESS.md create mode 100644 DOCS-UPDATE-SUMMARY.md diff --git a/.gradle-docs/CONFIGURATION.md b/.gradle-docs/CONFIGURATION.md index 26df214..11ee663 100644 --- a/.gradle-docs/CONFIGURATION.md +++ b/.gradle-docs/CONFIGURATION.md @@ -1,6 +1,8 @@ # Configuration Guide -Complete guide for configuring the Bearsampp Module Memcached build system. +Complete guide to configuring the Bearsampp Module Memcached build system. + +--- ## Table of Contents @@ -9,30 +11,23 @@ Complete guide for configuring the Bearsampp Module Memcached build system. - [Gradle Properties](#gradle-properties) - [Release Properties](#release-properties) - [Environment Variables](#environment-variables) -- [Advanced Configuration](#advanced-configuration) - -## Configuration Files +- [Build Paths](#build-paths) +- [Archive Configuration](#archive-configuration) +- [Configuration Examples](#configuration-examples) +- [Best Practices](#best-practices) -### Overview +--- -The build system uses three main configuration files: +## Configuration Files -| File | Purpose | Format | Required | -|------------------------|------------------------------------------|--------------|----------| -| `build.properties` | Bundle configuration | Properties | Yes | -| `gradle.properties` | Gradle build settings | Properties | Yes | -| `releases.properties` | Release history and URLs | Properties | Yes | -| `settings.gradle` | Gradle project settings | Groovy | Yes | +The build system uses several configuration files: -### File Locations - -``` -module-memcached/ -├── build.properties # Bundle configuration -├── gradle.properties # Gradle settings -├── releases.properties # Release history -└── settings.gradle # Project settings -``` +| File | Purpose | Required | +|-----------------------|------------------------------------------|----------| +| `build.properties` | Main build configuration | Yes | +| `gradle.properties` | Gradle-specific settings | No | +| `releases.properties` | Release history tracking | No | +| `settings.gradle` | Gradle project settings | Yes | --- @@ -40,162 +35,105 @@ module-memcached/ ### File: build.properties -Main configuration file for bundle settings. +Main configuration file for the build system. -**Location:** `E:/Bearsampp-development/module-memcached/build.properties` +**Location**: `build.properties` (project root) -**Format:** Java Properties +**Format**: Java properties file -### Properties Reference +**Required Properties**: -#### bundle.name +| Property | Description | Example Value | Required | +|-------------------|--------------------------------------|----------------|----------| +| `bundle.name` | Name of the bundle | `memcached` | Yes | +| `bundle.release` | Release version | `2025.8.20` | Yes | +| `bundle.type` | Type of bundle | `bins` | Yes | +| `bundle.format` | Archive format (7z or zip) | `7z` | Yes | -Bundle name identifier. +**Optional Properties**: -| Property | Type | Required | Default | Example | -|------------------|----------|----------|--------------|--------------| -| `bundle.name` | String | Yes | `memcached` | `memcached` | +| Property | Description | Example Value | Default | +|-------------------|--------------------------------------|----------------|---------| +| `build.path` | Custom build output path | `C:/Bearsampp-build` | `{parent}/bearsampp-build` | -**Description:** Identifies the bundle name used in file naming and directory structure. +**Example**: -**Usage:** ```properties -bundle.name = memcached -``` - -**Impact:** -- Used in archive naming: `bearsampp-${bundle.name}-${version}-${release}.7z` -- Used in directory naming: `bin/${bundle.name}${version}/` -- Used in display output +# Bundle configuration +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z ---- - -#### bundle.release - -Release date identifier. - -| Property | Type | Required | Default | Example | -|------------------|----------|----------|--------------|--------------| -| `bundle.release` | String | Yes | - | `2025.8.20` | - -**Description:** Release date in YYYY.M.D format. - -**Usage:** -```properties -bundle.release = 2025.8.20 +# Optional: Custom build path +# build.path = C:/Bearsampp-build ``` -**Format:** `YYYY.M.D` or `YYYY.MM.DD` - -**Examples:** -- `2025.8.20` - August 20, 2025 -- `2025.12.1` - December 1, 2025 -- `2024.3.30` - March 30, 2024 +### Property Details -**Impact:** -- Used in archive naming -- Used in release URLs -- Used in version tracking - ---- +#### bundle.name -#### bundle.type +The name of the module bundle. -Bundle type classification. +- **Type**: String +- **Required**: Yes +- **Default**: None +- **Example**: `memcached` +- **Usage**: Used in archive names and directory paths -| Property | Type | Required | Default | Example | -|------------------|----------|----------|--------------|--------------| -| `bundle.type` | String | Yes | `bins` | `bins` | +#### bundle.release -**Description:** Classifies the bundle type. +The release version for the build. -**Usage:** -```properties -bundle.type = bins -``` +- **Type**: String (date format recommended: YYYY.M.D) +- **Required**: Yes +- **Default**: None +- **Example**: `2025.8.20` +- **Usage**: Used in archive names and output directories -**Valid Values:** -- `bins` - Binary distribution -- `apps` - Application distribution -- `tools` - Tool distribution +#### bundle.type -**Impact:** -- Used for categorization -- May affect build process in future versions +The type of bundle being built. ---- +- **Type**: String +- **Required**: Yes +- **Default**: None +- **Allowed Values**: `bins`, `tools` +- **Example**: `bins` +- **Usage**: Determines output directory structure #### bundle.format -Archive format for release packages. +The archive format for the release package. -| Property | Type | Required | Default | Example | -|------------------|----------|----------|--------------|--------------| -| `bundle.format` | String | Yes | `7z` | `7z` | +- **Type**: String +- **Required**: Yes +- **Default**: None +- **Allowed Values**: `7z`, `zip` +- **Example**: `7z` +- **Usage**: Determines compression format and tool used -**Description:** Specifies the archive format for release packages. +**Format Comparison**: -**Usage:** -```properties -bundle.format = 7z -``` - -**Valid Values:** -- `7z` - 7-Zip format (recommended) -- `zip` - ZIP format - -**Impact:** -- Determines compression tool used -- Affects archive file extension -- Impacts compression ratio - -**Requirements:** -- `7z` format requires 7-Zip installed and in PATH -- `zip` format uses Gradle's built-in zip task - ---- +| Format | Compression | Speed | Tool Required | Recommended | +|--------|-------------|--------|---------------|-------------| +| `7z` | Excellent | Slower | 7-Zip | Yes | +| `zip` | Good | Faster | Built-in | No | -#### build.path (Optional) +#### build.path -Custom build output directory. +Custom build output path (optional). -| Property | Type | Required | Default | Example | -|------------------|----------|----------|--------------------------------------|----------------------------| -| `build.path` | String | No | `${user.home}/Bearsampp-build` | `C:/Bearsampp-build` | +- **Type**: String (absolute path) +- **Required**: No +- **Default**: `{parent}/bearsampp-build` +- **Example**: `C:/Bearsampp-build` +- **Usage**: Override default build output location -**Description:** Specifies custom build output directory. - -**Usage:** -```properties -build.path = C:/Bearsampp-build -``` - -**Default Behavior:** -If not specified, uses `${user.home}/Bearsampp-build` - -**Directory Structure:** -``` -${build.path}/ -├── tmp/ # Temporary build files -│ └── memcached1.6.39/ # Prepared bundle -└── release/ # Release archives - └── bearsampp-memcached-1.6.39-2025.8.20.7z -``` - ---- - -### Complete Example - -```properties -# Bundle Configuration -bundle.name = memcached -bundle.release = 2025.8.20 -bundle.type = bins -bundle.format = 7z - -# Optional: Custom build path -#build.path = C:/Bearsampp-build -``` +**Priority Order**: +1. `build.properties` value +2. `BEARSAMPP_BUILD_PATH` environment variable +3. Default: `{parent}/bearsampp-build` --- @@ -203,544 +141,411 @@ bundle.format = 7z ### File: gradle.properties -Gradle build system configuration. - -**Location:** `E:/Bearsampp-development/module-memcached/gradle.properties` - -**Format:** Java Properties - -### Properties Reference - -#### org.gradle.daemon +Gradle-specific configuration settings. -Enable Gradle daemon for faster builds. +**Location**: `gradle.properties` (project root) -| Property | Type | Required | Default | Example | -|--------------------------|-----------|----------|--------------|--------------| -| `org.gradle.daemon` | Boolean | No | `true` | `true` | +**Format**: Java properties file -**Description:** Enables Gradle daemon for improved build performance. +**Recommended Settings**: -**Usage:** ```properties +# Gradle daemon configuration org.gradle.daemon=true -``` - -**Benefits:** -- Faster build times -- Reduced startup overhead -- Improved incremental builds - ---- - -#### org.gradle.parallel - -Enable parallel task execution. - -| Property | Type | Required | Default | Example | -|--------------------------|-----------|----------|--------------|--------------| -| `org.gradle.parallel` | Boolean | No | `true` | `true` | +org.gradle.parallel=true +org.gradle.caching=true -**Description:** Enables parallel execution of independent tasks. +# JVM settings +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -**Usage:** -```properties -org.gradle.parallel=true +# Console output +org.gradle.console=auto +org.gradle.warning.mode=all ``` -**Benefits:** -- Faster builds on multi-core systems -- Better resource utilization +### Property Details ---- +#### Daemon Settings -#### org.gradle.caching +| Property | Description | Recommended | +|-----------------------|--------------------------------------|-------------| +| `org.gradle.daemon` | Enable Gradle daemon | `true` | +| `org.gradle.parallel` | Enable parallel execution | `true` | +| `org.gradle.caching` | Enable build caching | `true` | -Enable build caching. +#### JVM Settings -| Property | Type | Required | Default | Example | -|--------------------------|-----------|----------|--------------|--------------| -| `org.gradle.caching` | Boolean | No | `true` | `true` | +| Property | Description | Recommended | +|---------------------------|----------------------------------|-------------| +| `org.gradle.jvmargs` | JVM arguments for Gradle | `-Xmx2g -XX:MaxMetaspaceSize=512m` | -**Description:** Enables Gradle build cache for faster incremental builds. +**Common JVM Arguments**: +- `-Xmx2g` - Maximum heap size (2GB) +- `-Xms512m` - Initial heap size (512MB) +- `-XX:MaxMetaspaceSize=512m` - Maximum metaspace size +- `-XX:+HeapDumpOnOutOfMemoryError` - Create heap dump on OOM -**Usage:** -```properties -org.gradle.caching=true -``` +#### Console Settings -**Benefits:** -- Reuses outputs from previous builds -- Significantly faster incremental builds -- Shared cache across projects +| Property | Description | Options | +|---------------------------|----------------------------------|---------| +| `org.gradle.console` | Console output mode | `auto`, `plain`, `rich`, `verbose` | +| `org.gradle.warning.mode` | Warning display mode | `all`, `summary`, `none` | --- -#### org.gradle.configureondemand - -Enable configuration on demand. - -| Property | Type | Required | Default | Example | -|----------------------------------|-----------|----------|--------------|--------------| -| `org.gradle.configureondemand` | Boolean | No | `true` | `true` | - -**Description:** Configures only necessary projects. - -**Usage:** -```properties -org.gradle.configureondemand=true -``` +## Release Properties -**Benefits:** -- Faster configuration phase -- Reduced memory usage +### File: releases.properties ---- +Tracks release history and metadata. -#### org.gradle.jvmargs +**Location**: `releases.properties` (project root) -JVM arguments for Gradle. +**Format**: Java properties file -| Property | Type | Required | Default | Example | -|--------------------------|-----------|----------|--------------|----------------------------| -| `org.gradle.jvmargs` | String | No | - | `-Xmx2048m -Xms512m` | +**Purpose**: Historical tracking of releases -**Description:** Specifies JVM arguments for Gradle daemon. +**Example**: -**Usage:** ```properties -org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m +# Release history +2025.8.20 = Released on 2025-08-20 +2025.7.15 = Released on 2025-07-15 +2025.6.10 = Released on 2025-06-10 ``` -**Common Arguments:** -- `-Xmx2048m` - Maximum heap size (2GB) -- `-Xms512m` - Initial heap size (512MB) -- `-XX:MaxMetaspaceSize=512m` - Maximum metaspace size - --- -### Complete Example +## Environment Variables -```properties -# Gradle Build Configuration +The build system supports several environment variables: -# Enable daemon for faster builds -org.gradle.daemon=true +### BEARSAMPP_BUILD_PATH -# Enable parallel execution -org.gradle.parallel=true +Override the default build output path. -# Enable build caching -org.gradle.caching=true +- **Type**: String (absolute path) +- **Required**: No +- **Default**: `{parent}/bearsampp-build` +- **Example**: `C:/Bearsampp-build` +- **Priority**: 2 (after build.properties, before default) -# Enable configuration on demand -org.gradle.configureondemand=true +**Usage**: -# JVM arguments -org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m +```bash +# Windows (PowerShell) +$env:BEARSAMPP_BUILD_PATH = "C:/Bearsampp-build" +gradle release -PbundleVersion=1.6.29 -# Console output -org.gradle.console=rich +# Windows (CMD) +set BEARSAMPP_BUILD_PATH=C:/Bearsampp-build +gradle release -PbundleVersion=1.6.29 -# Warning mode -org.gradle.warning.mode=all +# Linux/macOS +export BEARSAMPP_BUILD_PATH=/opt/bearsampp-build +gradle release -PbundleVersion=1.6.29 ``` ---- +### 7Z_HOME -## Release Properties +Specify the 7-Zip installation directory. -### File: releases.properties +- **Type**: String (absolute path) +- **Required**: No (if 7-Zip is in PATH or standard location) +- **Example**: `C:/Program Files/7-Zip` -Historical release information and download URLs. +**Usage**: -**Location:** `E:/Bearsampp-development/module-memcached/releases.properties` - -**Format:** Java Properties - -### Format +```bash +# Windows (PowerShell) +$env:7Z_HOME = "C:/Program Files/7-Zip" +gradle release -PbundleVersion=1.6.29 -```properties - = +# Windows (CMD) +set 7Z_HOME=C:/Program Files/7-Zip +gradle release -PbundleVersion=1.6.29 ``` -### Properties Reference +### JAVA_HOME -Each entry represents a released version: +Specify the Java installation directory. -| Key | Value | Description | -|-------------|------------------------------------------|---------------------------------------| -| Version | Download URL | GitHub release download link | +- **Type**: String (absolute path) +- **Required**: Yes (usually set by Java installer) +- **Example**: `C:/Program Files/Java/jdk-17` -### Example Entries +**Usage**: -```properties -1.6.39 = https://github.com/Bearsampp/module-memcached/releases/download/2025.8.20/bearsampp-memcached-1.6.39-2025.8.20.7z -1.6.38 = https://github.com/Bearsampp/module-memcached/releases/download/2025.4.19/bearsampp-memcached-1.6.38-2025.4.19.7z -1.6.36 = https://github.com/Bearsampp/module-memcached/releases/download/2025.2.11/bearsampp-memcached-1.6.36-2025.2.11.7z +```bash +# Windows (PowerShell) +$env:JAVA_HOME = "C:/Program Files/Java/jdk-17" +gradle release -PbundleVersion=1.6.29 ``` -### URL Format +--- + +## Build Paths + +The build system uses a structured directory layout: + +### Default Path Structure ``` -https://github.com/Bearsampp/module-memcached/releases/download/{release_date}/bearsampp-memcached-{version}-{release_date}.7z +bearsampp-build/ # Build base directory +├── tmp/ # Temporary build files +│ ├── bundles_prep/ # Preparation directory +│ │ └── bins/memcached/ # Prepared bundles +│ │ └── memcached1.6.29/ # Version-specific prep +│ └── bundles_build/ # Build staging directory +│ └── bins/memcached/ # Staged bundles +│ └── memcached1.6.29/ # Version-specific build +└── bins/memcached/ # Final output directory + └── 2025.8.20/ # Release version + ├── bearsampp-memcached-1.6.29-2025.8.20.7z + ├── bearsampp-memcached-1.6.29-2025.8.20.7z.md5 + ├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha1 + ├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha256 + └── bearsampp-memcached-1.6.29-2025.8.20.7z.sha512 ``` -**Components:** -- `{release_date}` - Release date from `bundle.release` -- `{version}` - Memcached version number -- `.7z` - Archive extension +### Path Variables -### Adding New Releases +| Variable | Description | Example | +|-----------------------|--------------------------------------|---------| +| `buildBasePath` | Base build directory | `E:/Bearsampp-development/bearsampp-build` | +| `buildTmpPath` | Temporary files directory | `{buildBasePath}/tmp` | +| `bundleTmpPrepPath` | Bundle preparation directory | `{buildTmpPath}/bundles_prep/bins/memcached` | +| `bundleTmpBuildPath` | Bundle build staging directory | `{buildTmpPath}/bundles_build/bins/memcached` | +| `buildBinsPath` | Final output directory | `{buildBasePath}/bins/memcached/{bundle.release}` | -When creating a new release: +### Customizing Build Paths -1. Build the release package -2. Upload to GitHub releases -3. Add entry to `releases.properties`: +**Method 1: build.properties** ```properties -1.6.40 = https://github.com/Bearsampp/module-memcached/releases/download/2025.9.15/bearsampp-memcached-1.6.40-2025.9.15.7z +build.path = C:/Custom/Build/Path ``` -### Sorting +**Method 2: Environment Variable** -Entries should be sorted by version number (ascending): - -```properties -1.6.15 = ... -1.6.17 = ... -1.6.18 = ... -1.6.39 = ... +```bash +export BEARSAMPP_BUILD_PATH=/opt/custom/build ``` ---- - -## Environment Variables +**Method 3: Default** -### Required Variables +Uses `{parent}/bearsampp-build` automatically. -#### JAVA_HOME +--- -Java installation directory. +## Archive Configuration -| Variable | Required | Description | Example | -|--------------|----------|---------------------------------------|----------------------------------------| -| `JAVA_HOME` | Yes | Java JDK installation directory | `C:\Program Files\Java\jdk-17.0.2` | +### Archive Naming -**Usage:** -```bash -# Windows -set JAVA_HOME=C:\Program Files\Java\jdk-17.0.2 +Archives follow this naming convention: -# PowerShell -$env:JAVA_HOME="C:\Program Files\Java\jdk-17.0.2" ``` - -**Verification:** -```bash -echo %JAVA_HOME% -java -version +bearsampp-{bundle.name}-{version}-{bundle.release}.{bundle.format} ``` ---- - -#### PATH +**Examples**: +- `bearsampp-memcached-1.6.29-2025.8.20.7z` +- `bearsampp-memcached-1.6.39-2025.8.20.zip` -System path including required tools. +### Archive Structure -| Variable | Required | Description | Example | -|--------------|----------|---------------------------------------|----------------------------------------| -| `PATH` | Yes | System path with 7-Zip | `C:\Program Files\7-Zip;...` | - -**Required in PATH:** -- 7-Zip (for 7z format) -- Java (for Gradle) -- Gradle (optional, can use wrapper) - -**Usage:** -```bash -# Windows -set PATH=%PATH%;C:\Program Files\7-Zip +Archives contain a top-level version folder: -# PowerShell -$env:PATH="$env:PATH;C:\Program Files\7-Zip" ``` - -**Verification:** -```bash -7z -gradle --version +bearsampp-memcached-1.6.29-2025.8.20.7z +└── memcached1.6.29/ ← Version folder at root + ├── memcached.exe + ├── memcached.conf + └── ... ``` ---- - -### Optional Variables +### Hash Files -#### GRADLE_HOME +Each archive is accompanied by hash files: -Gradle installation directory. +| File Extension | Algorithm | Purpose | +|----------------|-----------|---------| +| `.md5` | MD5 | Quick verification | +| `.sha1` | SHA-1 | Legacy compatibility | +| `.sha256` | SHA-256 | Recommended verification | +| `.sha512` | SHA-512 | Maximum security | -| Variable | Required | Description | Example | -|----------------|----------|---------------------------------------|----------------------------------------| -| `GRADLE_HOME` | No | Gradle installation directory | `C:\Gradle\gradle-8.5` | +**Hash File Format**: -**Usage:** -```bash -# Windows -set GRADLE_HOME=C:\Gradle\gradle-8.5 -set PATH=%PATH%;%GRADLE_HOME%\bin +``` +{hash} {filename} ``` ---- - -#### GRADLE_USER_HOME - -Gradle user home directory. - -| Variable | Required | Description | Example | -|----------------------|----------|---------------------------------------|----------------------------------------| -| `GRADLE_USER_HOME` | No | Gradle cache and config directory | `C:\Users\troy\.gradle` | - -**Default:** `${user.home}/.gradle` +**Example** (`.sha256`): -**Usage:** -```bash -# Windows -set GRADLE_USER_HOME=C:\Users\troy\.gradle +``` +a1b2c3d4e5f6... bearsampp-memcached-1.6.29-2025.8.20.7z ``` --- -## Advanced Configuration - -### Custom Build Paths +## Configuration Examples -Override default build paths: +### Example 1: Standard Configuration ```properties # build.properties -build.path = D:/CustomBuild -``` - -**Directory Structure:** -``` -D:/CustomBuild/ -├── tmp/ -│ └── memcached1.6.39/ -└── release/ - └── bearsampp-memcached-1.6.39-2025.8.20.7z +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z ``` ---- - -### Performance Tuning - -Optimize Gradle performance: - ```properties # gradle.properties - -# Increase heap size for large builds -org.gradle.jvmargs=-Xmx4096m -Xms1024m - -# Enable all performance features org.gradle.daemon=true org.gradle.parallel=true org.gradle.caching=true -org.gradle.configureondemand=true - -# File system watching (Gradle 7.0+) -org.gradle.vfs.watch=true +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m ``` ---- - -### Network Configuration - -Configure network settings: +### Example 2: Custom Build Path ```properties -# gradle.properties +# build.properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +build.path = C:/Bearsampp-build +``` -# Proxy settings -systemProp.http.proxyHost=proxy.company.com -systemProp.http.proxyPort=8080 -systemProp.https.proxyHost=proxy.company.com -systemProp.https.proxyPort=8080 +### Example 3: ZIP Format -# Proxy authentication -systemProp.http.proxyUser=username -systemProp.http.proxyPassword=password +```properties +# build.properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = zip ``` ---- - -### Logging Configuration +### Example 4: Development Configuration -Configure logging levels: +```properties +# build.properties +bundle.name = memcached +bundle.release = 2025.8.20-dev +bundle.type = bins +bundle.format = zip +build.path = C:/Dev/Bearsampp-build +``` ```properties # gradle.properties - -# Console output -org.gradle.console=rich - -# Logging level -org.gradle.logging.level=lifecycle - -# Warning mode +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g +org.gradle.console=verbose org.gradle.warning.mode=all ``` -**Console Options:** -- `auto` - Automatic detection -- `plain` - Plain text output -- `rich` - Rich console output (default) -- `verbose` - Verbose output - -**Warning Modes:** -- `all` - Show all warnings -- `fail` - Fail on warnings -- `summary` - Show warning summary -- `none` - Suppress warnings - --- -### Build Cache Configuration - -Configure build cache: - -```groovy -// settings.gradle - -buildCache { - local { - enabled = true - directory = file("${rootDir}/.gradle/build-cache") - removeUnusedEntriesAfterDays = 30 - } - - remote(HttpBuildCache) { - enabled = false - url = 'https://cache.example.com/' - push = false - } -} -``` +## Best Practices ---- +### Configuration Management -### Multi-Project Configuration +1. **Version Control**: Commit `build.properties` and `gradle.properties` +2. **Documentation**: Document any custom configurations +3. **Consistency**: Use consistent naming and versioning +4. **Validation**: Run `gradle validateProperties` after changes -For multi-project builds: +### Build Properties -```groovy -// settings.gradle +1. **Release Versioning**: Use date format (YYYY.M.D) for `bundle.release` +2. **Archive Format**: Prefer `7z` for better compression +3. **Build Path**: Use absolute paths for `build.path` +4. **Comments**: Add comments to explain custom settings -rootProject.name = 'module-memcached' +### Gradle Properties -// Include subprojects -// include 'subproject1' -// include 'subproject2' +1. **Enable Daemon**: Always set `org.gradle.daemon=true` +2. **Parallel Execution**: Enable for faster builds +3. **Caching**: Enable for incremental builds +4. **Heap Size**: Adjust based on available memory -// Enable features -enableFeaturePreview('STABLE_CONFIGURATION_CACHE') -enableFeaturePreview('TYPESAFE_PROJECT_ACCESSORS') -``` +### Environment Variables ---- - -## Configuration Validation - -### Validate Configuration - -```bash -# Validate build.properties -gradle validateProperties - -# Verify environment -gradle verify +1. **Persistence**: Set in system environment for permanent use +2. **Documentation**: Document required environment variables +3. **Validation**: Verify environment variables are set correctly +4. **Priority**: Understand precedence order -# Display configuration -gradle info -``` - -### Configuration Checklist +### Path Configuration -- [ ] `build.properties` exists and is valid -- [ ] `gradle.properties` exists and is configured -- [ ] `releases.properties` exists and is up-to-date -- [ ] `settings.gradle` exists -- [ ] `JAVA_HOME` is set correctly -- [ ] 7-Zip is in PATH -- [ ] Gradle is installed or wrapper is present -- [ ] `bin/` directory contains version folders +1. **Absolute Paths**: Always use absolute paths +2. **Forward Slashes**: Use `/` instead of `\` for cross-platform compatibility +3. **No Trailing Slash**: Don't end paths with `/` +4. **Validation**: Ensure paths exist and are writable --- ## Troubleshooting Configuration -### Common Issues +### Issue: Invalid Properties -#### Issue: Property not found +**Symptom**: `build.properties validation failed` -**Error:** -``` -Property 'bundle.name' not found -``` +**Solution**: +1. Run `gradle validateProperties` +2. Check for missing required properties +3. Verify property values are valid +4. Check for typos in property names -**Solution:** -Check `build.properties` contains all required properties: -```properties -bundle.name = memcached -bundle.release = 2025.8.20 -bundle.type = bins -bundle.format = 7z -``` +### Issue: Build Path Not Found ---- +**Symptom**: `Build path not found` or permission errors -#### Issue: Invalid property value +**Solution**: +1. Verify path exists and is writable +2. Use absolute paths +3. Check environment variable is set correctly +4. Ensure no trailing slashes -**Error:** -``` -Invalid value for bundle.format: 'rar' -``` +### Issue: 7-Zip Not Found -**Solution:** -Use valid values: -```properties -bundle.format = 7z # or 'zip' -``` +**Symptom**: `7-Zip executable not found!` ---- +**Solution**: +1. Install 7-Zip from https://www.7-zip.org/ +2. Set `7Z_HOME` environment variable +3. Or change `bundle.format=zip` in build.properties -#### Issue: Path not found +### Issue: Gradle Daemon Issues -**Error:** -``` -Build path not found: C:/Invalid/Path -``` +**Symptom**: Slow builds or memory errors -**Solution:** -1. Check path exists -2. Use forward slashes or escaped backslashes -3. Remove `build.path` to use default +**Solution**: +1. Stop daemon: `gradle --stop` +2. Adjust heap size in gradle.properties +3. Disable daemon temporarily: `gradle --no-daemon` -```properties -# Correct -build.path = C:/Bearsampp-build +--- -# Also correct -build.path = C:\\Bearsampp-build +## Support -# Use default -#build.path = C:/Bearsampp-build -``` +For configuration issues: + +- **Documentation**: [README.md](README.md) +- **Tasks Reference**: [TASKS.md](TASKS.md) +- **GitHub Issues**: https://github.com/bearsampp/module-memcached/issues +- **Bearsampp Issues**: https://github.com/bearsampp/bearsampp/issues --- -**Last Updated:** 2025-08-20 -**Version:** 2025.8.20 -**Maintainer:** Bearsampp Team +**Last Updated**: 2025-08-20 +**Version**: 2025.8.20 +**Build System**: Pure Gradle diff --git a/.gradle-docs/CONFIGURATION_SUMMARY.md b/.gradle-docs/CONFIGURATION_SUMMARY.md deleted file mode 100644 index c276c6f..0000000 --- a/.gradle-docs/CONFIGURATION_SUMMARY.md +++ /dev/null @@ -1,271 +0,0 @@ -# Configuration Summary - -## Overview - -The Bearsampp Memcached module build system is configured through the `build.properties` file and various Gradle project settings. - -## build.properties - -The main configuration file for the build system. - -### Location -``` -D:/Bearsampp-dev/module-memcached/build.properties -``` - -### Properties - -#### bundle.name -- **Type**: String -- **Default**: `memcached` -- **Description**: The name of the bundle/module -- **Usage**: Used in directory names, archive names, and output paths - -```properties -bundle.name = memcached -``` - -#### bundle.release -- **Type**: Version String -- **Default**: `2025.8.20` -- **Description**: The release version of the build configuration -- **Usage**: Project version, metadata - -```properties -bundle.release = 2025.8.20 -``` - -#### bundle.type -- **Type**: String -- **Default**: `bins` -- **Options**: `bins`, `tools`, `apps` -- **Description**: The type of bundle being built -- **Usage**: Categorization, metadata - -```properties -bundle.type = bins -``` - -#### bundle.format -- **Type**: String -- **Default**: `7z` -- **Options**: `7z`, `zip` -- **Description**: Archive format for release packages -- **Usage**: Determines compression method and file extension - -```properties -bundle.format = 7z -``` - -#### build.path (Optional) -- **Type**: Path String -- **Default**: `C:/Bearsampp-build` -- **Description**: Output directory for built releases -- **Usage**: Destination for release archives and hash files - -```properties -#build.path = C:/Bearsampp-build -``` - -## Gradle Configuration - -### Project Settings - -Defined in `build.gradle`: - -```groovy -group = 'com.bearsampp.modules' -version = buildProps.getProperty('bundle.release', '1.0.0') -description = "Bearsampp Module - ${buildProps.getProperty('bundle.name', 'memcached')}" -``` - -### Path Configuration - -```groovy -ext { - projectBasedir = projectDir.absolutePath - rootDir = projectDir.parent - devPath = file("${rootDir}/dev").absolutePath - buildPropertiesFile = file('build.properties').absolutePath - - // Bundle properties from build.properties - bundleName = buildProps.getProperty('bundle.name', 'memcached') - bundleRelease = buildProps.getProperty('bundle.release', '1.0.0') - bundleType = buildProps.getProperty('bundle.type', 'bins') - bundleFormat = buildProps.getProperty('bundle.format', '7z') - - // Build paths - bundleBuildPath = buildProps.getProperty('build.path', 'C:/Bearsampp-build') - bundleTmpPrepPath = "${bundleBuildPath}/tmp/prep" -} -``` - -## Directory Structure - -### Source Directories - -``` -D:/Bearsampp-dev/module-memcached/ -├── bin/ # Version directories -│ ├── memcached1.6.15/ -│ ├── memcached1.6.17/ -│ └── ... -└── bin/archived/ # Archived versions - ├── memcached1.6.10/ - └── ... -``` - -### Build Output Structure - -``` -C:/Bearsampp-build/ -├── memcached1.6.29/ -│ ├── memcached1.6.29.7z -│ ├── memcached1.6.29.7z.md5 -│ ├── memcached1.6.29.7z.sha1 -│ ├── memcached1.6.29.7z.sha256 -│ └── memcached1.6.29.7z.sha512 -└── tmp/ - └── prep/ - └── memcached1.6.29/ # Staging directory -``` - -## Environment Variables - -### 7Z_HOME (Optional) - -- **Description**: Path to 7-Zip installation directory -- **Usage**: Used to locate 7z.exe if not in standard locations -- **Example**: `C:/Program Files/7-Zip` - -```bash -# Windows -set 7Z_HOME=C:\Program Files\7-Zip - -# PowerShell -$env:7Z_HOME = "C:\Program Files\7-Zip" -``` - -## Task Configuration - -### Default Task - -```groovy -defaultTasks 'info' -``` - -Running `gradle` without arguments executes the `info` task. - -### Task Groups - -Tasks are organized into groups: - -- **build**: Build and package tasks -- **help**: Information and help tasks -- **verification**: Validation and verification tasks - -## Validation - -### Required Properties - -The following properties must be present in `build.properties`: - -- `bundle.name` -- `bundle.release` -- `bundle.type` -- `bundle.format` - -### Validation Command - -```bash -gradle validateProperties -``` - -This checks that all required properties are present and non-empty. - -## Customization - -### Changing Build Output Directory - -Edit `build.properties`: - -```properties -build.path = D:/MyCustomBuildPath -``` - -### Changing Archive Format - -Edit `build.properties`: - -```properties -bundle.format = zip -``` - -### Changing Bundle Type - -Edit `build.properties`: - -```properties -bundle.type = tools -``` - -## Best Practices - -### 1. Version Control - -- Commit `build.properties` to version control -- Document any custom settings -- Use comments for optional properties - -### 2. Path Configuration - -- Use absolute paths for `build.path` -- Ensure build directory has write permissions -- Keep build output separate from source - -### 3. Archive Format - -- Use `7z` for maximum compression -- Use `zip` for better compatibility -- Consider target platform requirements - -### 4. Validation - -- Run `gradle validateProperties` before builds -- Run `gradle verify` to check environment -- Test configuration changes - -## Troubleshooting - -### Property Not Found - -If you get "property not found" errors: - -1. Check `build.properties` exists -2. Verify property names are correct -3. Ensure no typos in property keys -4. Run `gradle validateProperties` - -### Path Issues - -If you get path-related errors: - -1. Check paths use forward slashes or escaped backslashes -2. Verify directories exist and are accessible -3. Check file permissions -4. Use absolute paths when possible - -### Format Issues - -If archive creation fails: - -1. Verify `bundle.format` is `7z` or `zip` -2. Check 7-Zip is installed (for 7z format) -3. Run `gradle verify` to check environment -4. Check available disk space - -## Related Documentation - -- **QUICK_REFERENCE.md** - Common commands and usage -- **FEATURE_SUMMARY.md** - Build system features -- **MODULES_UNTOUCHED_INTEGRATION.md** - Remote integration details diff --git a/.gradle-docs/FEATURE_SUMMARY.md b/.gradle-docs/FEATURE_SUMMARY.md deleted file mode 100644 index 2766014..0000000 --- a/.gradle-docs/FEATURE_SUMMARY.md +++ /dev/null @@ -1,111 +0,0 @@ -# Feature Summary - -## Overview - -The Bearsampp Memcached module uses a modern Gradle build system that provides comprehensive build automation, version management, and release packaging capabilities. - -## Key Features - -### 1. Native Gradle Build - -- **No Ant Dependency**: Pure Gradle implementation without legacy Ant integration -- **Modern Build System**: Leverages Gradle's caching, incremental builds, and parallel execution -- **Clean Architecture**: Well-organized task structure with clear separation of concerns - -### 2. Automatic Hash File Generation - -For each release archive, the build system automatically generates: -- **MD5** hash file (`.md5`) -- **SHA1** hash file (`.sha1`) -- **SHA256** hash file (`.sha256`) -- **SHA512** hash file (`.sha512`) - -This ensures integrity verification for all distributed packages. - -### 3. Modules-Untouched Integration - -- **Remote Version Fetching**: Automatically fetches available versions from the modules-untouched repository -- **Fallback Strategy**: Uses standard URL construction if remote fetch fails -- **Version Discovery**: Lists all available releases from the remote repository - -### 4. Flexible Version Management - -- **Multiple Source Directories**: Supports both `bin/` and `bin/archived/` directories -- **Version Discovery**: Automatically detects all available versions -- **Batch Building**: Build all versions with a single command - -### 5. Archive Format Support - -- **7-Zip Format**: Native support for `.7z` archives with maximum compression -- **ZIP Format**: Built-in Gradle ZIP support as alternative -- **Configurable**: Format can be changed in `build.properties` - -### 6. Comprehensive Verification - -- **Environment Checks**: Verifies Java version, required files, and dependencies -- **Configuration Validation**: Ensures all required properties are present -- **Integration Testing**: Tests modules-untouched connectivity - -### 7. Rich Task Set - -#### Build Tasks -- `release` - Build single version release -- `releaseAll` - Build all available versions -- `clean` - Clean build artifacts - -#### Help Tasks -- `info` - Display build configuration -- `listVersions` - List local versions -- `listReleases` - List remote versions -- `tasks` - List all available tasks - -#### Verification Tasks -- `verify` - Verify build environment -- `validateProperties` - Validate configuration -- `checkModulesUntouched` - Test remote integration - -### 8. User-Friendly Output - -- **Progress Indicators**: Clear progress messages during builds -- **Formatted Output**: Well-formatted tables and summaries -- **Error Messages**: Helpful error messages with suggestions -- **Build Summaries**: Detailed success/failure reports - -### 9. Configurable Build Paths - -- **Custom Build Directory**: Configurable output directory -- **Temporary Prep Directory**: Separate staging area for builds -- **Organized Output**: Structured output with version-specific directories - -### 10. Cross-Platform Compatibility - -- **Windows Support**: Native Windows path handling -- **PowerShell Compatible**: Works with PowerShell and CMD -- **Path Flexibility**: Handles various drive letters and path formats - -## Benefits - -### For Developers -- **Easy to Use**: Simple command-line interface -- **Fast Builds**: Gradle's incremental build support -- **Reliable**: Comprehensive error checking and validation - -### For Release Management -- **Automated Hashing**: No manual hash file creation -- **Batch Processing**: Build multiple versions efficiently -- **Version Tracking**: Clear version management and discovery - -### For Quality Assurance -- **Integrity Verification**: Multiple hash algorithms -- **Environment Validation**: Pre-build checks -- **Consistent Output**: Standardized release packages - -## Future Enhancements - -Potential areas for future improvement: -- Parallel version building -- Custom compression levels -- Additional archive formats -- Build caching optimization -- Release notes generation -- Automated testing integration diff --git a/.gradle-docs/INDEX.md b/.gradle-docs/INDEX.md index ca1cd42..9b639d2 100644 --- a/.gradle-docs/INDEX.md +++ b/.gradle-docs/INDEX.md @@ -1,110 +1,134 @@ -# Bearsampp Module Memcached - Documentation Index +# Documentation Index -Complete documentation for the Bearsampp Module Memcached build system. +Complete index of all Gradle build documentation for Bearsampp Module Memcached. + +--- + +## Quick Links + +| Document | Description | Link | +|-----------------------|--------------------------------------------------|-------------------------------| +| **Main Documentation**| Complete build system guide | [README.md](README.md) | +| **Task Reference** | All available Gradle tasks | [TASKS.md](TASKS.md) | +| **Configuration** | Configuration files and properties | [CONFIGURATION.md](CONFIGURATION.md) | +| **Migration Guide** | Ant to Gradle migration guide | [MIGRATION.md](MIGRATION.md) | + +--- ## Documentation Structure ``` .gradle-docs/ -├── INDEX.md # This file - Documentation index -├── README.md # Main documentation and quick start -├── TASKS.md # Complete task reference -├── CONFIGURATION.md # Configuration guide -├── RELEASE-PROCESS.md # Release process guide -└── MIGRATION-GUIDE.md # Migration guide from Ant to Gradle +├── INDEX.md # This file - Documentation index +├── README.md # Main documentation and quick start +├── TASKS.md # Complete task reference +├── CONFIGURATION.md # Configuration guide +└── MIGRATION.md # Ant to Gradle migration guide ``` -## Quick Navigation +--- + +## Getting Started -### Getting Started +### New Users -| Document | Description | Audience | -|--------------------------------------------------|------------------------------------------------|-------------------------| -| [README.md](README.md) | Main documentation and quick start | All users | +1. **Start Here**: [README.md](README.md) - Overview and quick start +2. **Verify Setup**: Run `gradle verify` to check environment +3. **List Tasks**: Run `gradle tasks` to see available tasks +4. **Build Release**: Run `gradle release -PbundleVersion=1.6.29` -### Reference Documentation +### Migrating from Ant -| Document | Description | Audience | -|--------------------------------------------------|------------------------------------------------|-------------------------| -| [TASKS.md](TASKS.md) | Complete task reference | Developers | -| [CONFIGURATION.md](CONFIGURATION.md) | Configuration guide | Developers, DevOps | -| [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Release process guide | Maintainers | -| [MIGRATION-GUIDE.md](MIGRATION-GUIDE.md) | Migration guide from Ant to Gradle | All users | +1. **Migration Guide**: [MIGRATION.md](MIGRATION.md) - Complete migration guide +2. **Command Mapping**: See command equivalents in migration guide +3. **File Changes**: Understand what changed from Ant to Gradle +4. **Troubleshooting**: Common migration issues and solutions + +### Advanced Users + +1. **Task Reference**: [TASKS.md](TASKS.md) - All tasks with examples +2. **Configuration**: [CONFIGURATION.md](CONFIGURATION.md) - Advanced configuration +3. **Custom Tasks**: Create custom tasks using Gradle DSL + +--- ## Documentation by Topic ### Build System -| Topic | Document | Section | -|-------------------------------|--------------------------------------------------|-----------------------------------| -| Overview | [README.md](README.md) | Build System | -| Architecture | [README.md](README.md) | Build System > Architecture | -| Build Flow | [README.md](README.md) | Build System > Build Flow | +| Topic | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Overview | README.md | Overview | +| Quick Start | README.md | Quick Start | +| Installation | README.md | Installation | +| Architecture | README.md | Architecture | ### Tasks -| Topic | Document | Section | -|-------------------------------|--------------------------------------------------|-----------------------------------| -| All Tasks | [TASKS.md](TASKS.md) | - | -| Build Tasks | [TASKS.md](TASKS.md) | Build Tasks | -| Verification Tasks | [TASKS.md](TASKS.md) | Verification Tasks | -| Help Tasks | [TASKS.md](TASKS.md) | Help Tasks | -| Documentation Tasks | [TASKS.md](TASKS.md) | Documentation Tasks | -| Task Dependencies | [TASKS.md](TASKS.md) | Task Dependencies | +| Topic | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Build Tasks | TASKS.md | Build Tasks | +| Verification Tasks | TASKS.md | Verification Tasks | +| Information Tasks | TASKS.md | Information Tasks | +| Task Examples | TASKS.md | Task Examples | ### Configuration -| Topic | Document | Section | -|-------------------------------|--------------------------------------------------|-----------------------------------| -| Configuration Files | [CONFIGURATION.md](CONFIGURATION.md) | Configuration Files | -| Build Properties | [CONFIGURATION.md](CONFIGURATION.md) | Build Properties | -| Gradle Properties | [CONFIGURATION.md](CONFIGURATION.md) | Gradle Properties | -| Release Properties | [CONFIGURATION.md](CONFIGURATION.md) | Release Properties | -| Environment Variables | [CONFIGURATION.md](CONFIGURATION.md) | Environment Variables | -| Advanced Configuration | [CONFIGURATION.md](CONFIGURATION.md) | Advanced Configuration | - -### Release Process - -| Topic | Document | Section | -|-------------------------------|--------------------------------------------------|-----------------------------------| -| Overview | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Overview | -| Prerequisites | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Prerequisites | -| Release Workflow | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Release Workflow | -| Step-by-Step Guide | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Step-by-Step Guide | -| Version Management | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Version Management | -| Publishing Releases | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Publishing Releases | -| Post-Release Tasks | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Post-Release Tasks | - -### Troubleshooting - -| Topic | Document | Section | -|-------------------------------|--------------------------------------------------|-----------------------------------| -| Common Issues | [README.md](README.md) | Troubleshooting | -| Configuration Issues | [CONFIGURATION.md](CONFIGURATION.md) | Troubleshooting Configuration | -| Release Issues | [RELEASE-PROCESS.md](RELEASE-PROCESS.md) | Troubleshooting | +| Topic | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Build Properties | CONFIGURATION.md | Build Properties | +| Gradle Properties | CONFIGURATION.md | Gradle Properties | +| Release Properties | CONFIGURATION.md | Release Properties | +| Environment Variables | CONFIGURATION.md | Environment Variables | + +### Migration + +| Topic | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Overview | MIGRATION.md | Overview | +| What Changed | MIGRATION.md | What Changed | +| Command Mapping | MIGRATION.md | Command Mapping | +| File Changes | MIGRATION.md | File Changes | +| Troubleshooting | MIGRATION.md | Troubleshooting | + +--- ## Common Tasks -### For New Users +### Building + +| Task | Document | Reference | +|-------------------------------------------|---------------|----------------------------------| +| Build a release | README.md | Quick Start | +| Build specific version | TASKS.md | release task | +| Build all versions | TASKS.md | releaseAll task | +| Clean build artifacts | TASKS.md | clean task | + +### Configuration + +| Task | Document | Reference | +|-------------------------------------------|---------------|----------------------------------| +| Configure build properties | CONFIGURATION.md | Build Properties | +| Configure build paths | CONFIGURATION.md | Build Paths | +| Configure archive format | CONFIGURATION.md | Archive Configuration | -1. Read [README.md](README.md) - Overview and quick start -2. Run `gradle info` - Display build information -3. Run `gradle verify` - Verify build environment -4. Run `gradle listVersions` - List available versions +### Verification -### For Developers +| Task | Document | Reference | +|-------------------------------------------|---------------|----------------------------------| +| Verify build environment | TASKS.md | verify task | +| Validate properties | TASKS.md | validateProperties task | +| Check modules-untouched | TASKS.md | checkModulesUntouched task | -1. Read [TASKS.md](TASKS.md) - Learn available tasks -2. Read [CONFIGURATION.md](CONFIGURATION.md) - Understand configuration -3. Run `gradle tasks` - List all tasks -4. Run `gradle release -PbundleVersion=1.6.39` - Build a release +### Information -### For Maintainers +| Task | Document | Reference | +|-------------------------------------------|---------------|----------------------------------| +| Display build info | TASKS.md | info task | +| List available versions | TASKS.md | listVersions task | +| List releases | TASKS.md | listReleases task | -1. Read [RELEASE-PROCESS.md](RELEASE-PROCESS.md) - Learn release process -2. Follow step-by-step guide for releases -3. Update documentation as needed -4. Monitor issues and feedback +--- ## Quick Reference @@ -114,17 +138,20 @@ Complete documentation for the Bearsampp Module Memcached build system. # Display build information gradle info -# List available versions -gradle listVersions +# List all available tasks +gradle tasks -# List all releases -gradle listReleases - -# Verify environment +# Verify build environment gradle verify -# Build release -gradle release -PbundleVersion=1.6.39 +# Build a release (interactive) +gradle release + +# Build a specific version (non-interactive) +gradle release -PbundleVersion=1.6.29 + +# Build all versions +gradle releaseAll # Clean build artifacts gradle clean @@ -132,182 +159,182 @@ gradle clean ### Essential Files -| File | Purpose | Documentation | -|------------------------|------------------------------------------|-----------------------------------------------| -| `build.gradle` | Main build script | [README.md](README.md) | -| `build.properties` | Bundle configuration | [CONFIGURATION.md](CONFIGURATION.md) | -| `gradle.properties` | Gradle settings | [CONFIGURATION.md](CONFIGURATION.md) | -| `releases.properties` | Release history | [CONFIGURATION.md](CONFIGURATION.md) | -| `settings.gradle` | Project settings | [README.md](README.md) | +| File | Purpose | +|-----------------------|------------------------------------------| +| `build.gradle` | Main Gradle build script | +| `settings.gradle` | Gradle project settings | +| `build.properties` | Build configuration | +| `gradle.properties` | Gradle-specific settings | +| `releases.properties` | Release history | ### Essential Directories -| Directory | Purpose | Documentation | -|-----------------------|------------------------------------------|-----------------------------------------------| -| `bin/` | Memcached version binaries | [README.md](README.md) | -| `.gradle-docs/` | Build documentation | This file | -| `build/` | Gradle build output | [README.md](README.md) | - -## Documentation Standards - -### Format - -All documentation uses Markdown format with: - -- Clear headings and structure -- Tables for reference information -- Code blocks for examples -- Consistent formatting - -### Tables - -Tables are aligned with consistent column widths: +| Directory | Purpose | +|-----------------------|------------------------------------------| +| `bin/` | Memcached version bundles | +| `bin/archived/` | Archived Memcached versions | +| `bearsampp-build/tmp/` | Temporary build files (external) | +| `bearsampp-build/bins/` | Final packaged archives (external) | +| `.gradle-docs/` | Gradle documentation | -```markdown -| Column 1 | Column 2 | Column 3 | -|---------------------|---------------------|---------------------| -| Value 1 | Value 2 | Value 3 | -``` - -### Code Blocks - -Code blocks specify the language: - -```markdown -```bash -gradle info -``` -``` - -### Links - -Internal links use relative paths: - -```markdown -[README.md](README.md) -``` - -External links use full URLs: +--- -```markdown -[Bearsampp](https://bearsampp.com) -``` +## Search by Keyword + +### A-C + +| Keyword | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Archive | CONFIGURATION.md | Archive Configuration | +| Architecture | README.md | Architecture | +| Build | TASKS.md | Build Tasks | +| Clean | TASKS.md | clean task | +| Configuration | CONFIGURATION.md | All sections | + +### D-M + +| Keyword | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Dependencies | README.md | Installation | +| Gradle | README.md | All sections | +| Hash Files | README.md | Architecture | +| Info | TASKS.md | info task | +| Installation | README.md | Installation | +| Migration | MIGRATION.md | All sections | +| Modules-Untouched | TASKS.md | checkModulesUntouched task | + +### P-Z + +| Keyword | Document | Section | +|-----------------------|-----------------------|----------------------------------| +| Properties | CONFIGURATION.md | Build Properties | +| Release | TASKS.md | release task | +| Tasks | TASKS.md | All sections | +| Troubleshooting | README.md, MIGRATION.md | Troubleshooting sections | +| Validation | TASKS.md | Verification Tasks | +| Verify | TASKS.md | verify task | +| Versions | TASKS.md | listVersions task | -## Maintenance +--- -### Updating Documentation +## Document Summaries -When updating documentation: +### README.md -1. Update relevant document(s) -2. Update this index if structure changes -3. Update version and date at bottom -4. Test all links -5. Verify formatting +**Purpose**: Main documentation and quick start guide -### Version Information +**Contents**: +- Overview of the Gradle build system +- Quick start guide with basic commands +- Installation instructions +- Complete task reference +- Configuration overview +- Architecture and build process flow +- Troubleshooting common issues +- Migration guide summary -All documentation includes version information: +**Target Audience**: All users, especially new users -```markdown --- -**Last Updated:** YYYY-MM-DD -**Version:** YYYY.M.D -**Maintainer:** Bearsampp Team -``` - -### Review Schedule - -Documentation should be reviewed: +### TASKS.md -- After each release -- When build system changes -- When new features are added -- Quarterly for accuracy +**Purpose**: Complete reference for all Gradle tasks -## Contributing to Documentation +**Contents**: +- Build tasks (release, releaseAll, clean) +- Verification tasks (verify, validateProperties, checkModulesUntouched) +- Information tasks (info, listVersions, listReleases) +- Task dependencies and execution order +- Task examples and usage patterns +- Task options and properties -### Guidelines +**Target Audience**: Developers and build engineers -- Use clear, concise language -- Provide examples -- Keep tables aligned -- Test all commands -- Update index when adding sections - -### Style Guide +--- -- Use present tense -- Use active voice -- Use consistent terminology -- Use proper Markdown formatting -- Include code examples +### CONFIGURATION.md -### Submitting Changes +**Purpose**: Configuration guide for build system -1. Fork the repository -2. Create a documentation branch -3. Make your changes -4. Test all examples -5. Submit a pull request +**Contents**: +- Configuration files overview +- Build properties reference +- Gradle properties reference +- Release properties reference +- Environment variables +- Build paths configuration +- Archive format configuration +- Configuration examples +- Best practices -## Support +**Target Audience**: Build engineers and advanced users -### Getting Help +--- -- Read the documentation -- Check troubleshooting sections -- Search GitHub issues -- Ask in discussions +### MIGRATION.md -### Reporting Issues +**Purpose**: Guide for migrating from Ant to Gradle -Report documentation issues at: +**Contents**: +- Migration overview +- What changed from Ant to Gradle +- Command mapping (Ant to Gradle) +- File changes +- Configuration changes +- Task equivalents +- Troubleshooting migration issues +- Benefits of migration +- Next steps for developers, CI/CD, and contributors -https://github.com/bearsampp/bearsampp/issues +**Target Audience**: Users migrating from Ant build system -Include: +--- -- Document name -- Section name -- Issue description -- Suggested improvement +## Version History -## External Resources +| Version | Date | Changes | +|---------------|------------|------------------------------------------| +| 2025.8.20 | 2025-08-20 | Initial Gradle documentation | +| | | - Created README.md | +| | | - Created TASKS.md | +| | | - Created CONFIGURATION.md | +| | | - Created MIGRATION.md | +| | | - Created INDEX.md | -### Gradle +--- -- [Gradle Documentation](https://docs.gradle.org/) -- [Gradle User Manual](https://docs.gradle.org/current/userguide/userguide.html) -- [Gradle Build Language Reference](https://docs.gradle.org/current/dsl/) +## Contributing -### Memcached +To contribute to the documentation: -- [Memcached Official](https://memcached.org/) -- [Memcached Documentation](https://github.com/memcached/memcached/wiki) -- [Memcached Downloads](https://memcached.org/downloads) +1. **Fork Repository**: Fork the module-memcached repository +2. **Edit Documentation**: Make changes to documentation files +3. **Follow Style**: Maintain consistent formatting and style +4. **Test Examples**: Verify all code examples work +5. **Submit PR**: Create pull request with changes -### Bearsampp +### Documentation Style Guide -- [Bearsampp Project](https://github.com/bearsampp/bearsampp) -- [Bearsampp Website](https://bearsampp.com) -- [Bearsampp Documentation](https://bearsampp.com/docs) +- Use Markdown formatting +- Include code examples +- Use tables for structured data +- Add links to related sections +- Keep language clear and concise +- Include practical examples -### Tools +--- -- [7-Zip](https://www.7-zip.org/) -- [Git](https://git-scm.com/) -- [Java](https://adoptium.net/) +## Support -## Document History +For documentation issues or questions: -| Version | Date | Changes | -|-------------|--------------|--------------------------------------------------| -| 2025.8.20 | 2025-08-20 | Initial documentation for pure Gradle build | +- **GitHub Issues**: https://github.com/bearsampp/module-memcached/issues +- **Bearsampp Issues**: https://github.com/bearsampp/bearsampp/issues +- **Documentation**: This directory (.gradle-docs/) --- -**Last Updated:** 2025-08-20 -**Version:** 2025.8.20 -**Maintainer:** Bearsampp Team +**Last Updated**: 2025-08-20 +**Version**: 2025.8.20 +**Total Documents**: 5 diff --git a/.gradle-docs/INTERACTIVE_MODE.md b/.gradle-docs/INTERACTIVE_MODE.md deleted file mode 100644 index 94d871d..0000000 --- a/.gradle-docs/INTERACTIVE_MODE.md +++ /dev/null @@ -1,275 +0,0 @@ -# Interactive Mode - -## Overview - -The `gradle release` task supports both interactive and non-interactive modes, allowing you to choose the most convenient method for building releases. - -## Non-Interactive Mode - -Specify the version directly on the command line: - -```bash -gradle release -PbundleVersion=1.6.29 -``` - -**Use when:** -- Running in CI/CD pipelines -- Automating builds with scripts -- You know exactly which version to build - -## Interactive Mode - -Run without specifying a version to enter interactive mode: - -```bash -gradle release -``` - -### Interactive Mode Flow - -1. **Display Available Versions** - ``` - ====================================================================== - Interactive Release Mode - ====================================================================== - - Available versions: - 1. 1.6.15 [bin] - 2. 1.6.17 [bin] - 3. 1.6.18 [bin] - 4. 1.6.21 [bin] - 5. 1.6.24 [bin] - 6. 1.6.29 [bin] - 7. 1.6.31 [bin] - 8. 1.6.32 [bin] - 9. 1.6.33 [bin] - 10. 1.6.36 [bin] - 11. 1.6.37 [bin] - 12. 1.6.38 [bin] - 13. 1.6.39 [bin] - - Enter version to build (index or version string): - ``` - -2. **Select Version** - - You can select a version in two ways: - - **Option A: By Index Number** - ``` - Enter version to build (index or version string): - 5 - ``` - This will select version 1.6.24 (the 5th in the list) - - **Option B: By Version String** - ``` - Enter version to build (index or version string): - 1.6.29 - ``` - This will select version 1.6.29 directly - -3. **Confirmation** - ``` - Selected version: 1.6.29 - - ====================================================================== - Building memcached 1.6.29 - ====================================================================== - ``` - -4. **Build Process** - The build proceeds normally with the selected version. - -## Features - -### Version Location Tags - -Each version shows where it's located: -- `[bin]` - Located in `bin/` directory -- `[bin/archived]` - Located in `bin/archived/` directory - -This helps you understand which versions are current and which are archived. - -### Input Validation - -The interactive mode validates your input: - -**Invalid Index:** -``` -Enter version to build (index or version string): -99 - -ERROR: Invalid selection index: 99 -Please choose a number between 1 and 13 or enter a version string. -``` - -**Invalid Version String:** -``` -Enter version to build (index or version string): -1.6.99 - -ERROR: Invalid version: 1.6.99 -Please choose from available versions: - - 1.6.15 - - 1.6.17 - ... -``` - -**Empty Input:** -``` -Enter version to build (index or version string): -[Enter] - -ERROR: No version selected. Please use non-interactive mode: - gradle release -PbundleVersion=X.X.X - -Available versions: 1.6.15, 1.6.17, 1.6.18, ... -``` - -### Fallback to Non-Interactive - -If interactive input fails (e.g., running in a non-interactive environment), the error message provides clear instructions for using non-interactive mode: - -``` -ERROR: Failed to read input. Please use non-interactive mode: - gradle release -PbundleVersion=X.X.X - -Available versions: 1.6.15, 1.6.17, 1.6.18, ... -``` - -## Use Cases - -### Interactive Mode is Best For: - -1. **Manual Builds** - - Building releases locally - - Testing different versions - - Exploring available versions - -2. **Development** - - Quick iteration during development - - Testing build process - - Verifying version availability - -3. **Learning** - - Understanding what versions are available - - Seeing the directory structure - - Getting familiar with the build system - -### Non-Interactive Mode is Best For: - -1. **Automation** - - CI/CD pipelines - - Build scripts - - Scheduled builds - -2. **Scripting** - - Batch processing - - Integration with other tools - - Automated testing - -3. **Remote Execution** - - SSH sessions without TTY - - Background jobs - - Cron jobs - -## PowerShell Note - -When using PowerShell with non-interactive mode, quote the parameter: - -```powershell -# Correct -gradle release "-PbundleVersion=1.6.29" - -# Incorrect (PowerShell interprets -P as a parameter) -gradle release -PbundleVersion=1.6.29 -``` - -In CMD, quotes are optional: - -```cmd -gradle release -PbundleVersion=1.6.29 -``` - -## Examples - -### Example 1: Interactive Selection by Index - -```bash -$ gradle release - -====================================================================== -Interactive Release Mode -====================================================================== - -Available versions: - 1. 1.6.29 [bin] - 2. 1.6.31 [bin] - 3. 1.6.32 [bin] - -Enter version to build (index or version string): -2 - -Selected version: 1.6.31 - -====================================================================== -Building memcached 1.6.31 -====================================================================== -... -``` - -### Example 2: Interactive Selection by Version String - -```bash -$ gradle release - -====================================================================== -Interactive Release Mode -====================================================================== - -Available versions: - 1. 1.6.29 [bin] - 2. 1.6.31 [bin] - 3. 1.6.32 [bin] - -Enter version to build (index or version string): -1.6.32 - -Selected version: 1.6.32 - -====================================================================== -Building memcached 1.6.32 -====================================================================== -... -``` - -### Example 3: Non-Interactive Mode - -```bash -$ gradle release -PbundleVersion=1.6.29 - -====================================================================== -Building memcached 1.6.29 -====================================================================== -... -``` - -## Comparison with Bruno Module - -The interactive mode implementation matches the bruno module's behavior: - -✅ Same interactive prompt format -✅ Same version selection methods (index or string) -✅ Same validation logic -✅ Same error messages -✅ Same fallback behavior -✅ Same location tags display - -This ensures consistency across all Bearsampp modules. - -## Related Documentation - -- **QUICK_REFERENCE.md** - Quick command reference -- **FEATURE_SUMMARY.md** - Overview of all features -- **README.md** - Main documentation diff --git a/.gradle-docs/MIGRATION-GUIDE.md b/.gradle-docs/MIGRATION-GUIDE.md deleted file mode 100644 index 1b105d2..0000000 --- a/.gradle-docs/MIGRATION-GUIDE.md +++ /dev/null @@ -1,603 +0,0 @@ -# Migration Guide: Ant to Pure Gradle - -Guide for migrating from the Ant-based build system to the pure Gradle build system. - -## Table of Contents - -- [Overview](#overview) -- [What Changed](#what-changed) -- [Command Mapping](#command-mapping) -- [Configuration Changes](#configuration-changes) -- [Benefits](#benefits) -- [Migration Steps](#migration-steps) -- [Troubleshooting](#troubleshooting) - -## Overview - -The Bearsampp Module Memcached build system has been migrated from a hybrid Ant/Gradle system to a pure Gradle build system. - -### Migration Summary - -| Aspect | Before (Ant/Gradle) | After (Pure Gradle) | -|----------------------|----------------------------------------|----------------------------------------| -| Build System | Hybrid Ant + Gradle | Pure Gradle | -| Build Files | `build.xml`, `build.gradle` | `build.gradle` only | -| Task Execution | Ant tasks via Gradle wrapper | Native Gradle tasks | -| Dependencies | External Ant build files | Self-contained | -| Documentation | Scattered | Centralized in `.gradle-docs/` | - -## What Changed - -### Removed - -| Item | Reason | -|------------------------------|--------------------------------------------------| -| `build.xml` | Replaced by native Gradle tasks | -| Ant task imports | No longer needed | -| External Ant dependencies | Build is now self-contained | -| Ant-specific properties | Converted to Gradle properties | - -### Added - -| Item | Purpose | -|------------------------------|--------------------------------------------------| -| Pure Gradle tasks | Native Gradle implementation | -| `.gradle-docs/` | Comprehensive documentation | -| Enhanced output formatting | Better user experience | -| Build verification | Environment checking | - -### Modified - -| Item | Change | -|------------------------------|--------------------------------------------------| -| `build.gradle` | Removed Ant imports, added native tasks | -| `settings.gradle` | Removed Ant references | -| `gradle.properties` | Added performance optimizations | -| `README.md` | Updated with new documentation structure | - -## Command Mapping - -### Build Commands - -| Old Command (Ant) | New Command (Gradle) | -|-------------------------------------------|---------------------------------------------------| -| `ant release -Dinput.bundle=1.6.39` | `gradle release -PbundleVersion=1.6.39` | -| `ant clean` | `gradle clean` | -| `ant -projecthelp` | `gradle tasks` | - -### Information Commands - -| Old Command (Ant) | New Command (Gradle) | -|-------------------------------------------|---------------------------------------------------| -| N/A | `gradle info` | -| N/A | `gradle listVersions` | -| N/A | `gradle listReleases` | -| N/A | `gradle verify` | -| N/A | `gradle validateProperties` | - -### Interactive vs Non-Interactive - -**Old (Ant):** -```bash -# Interactive only -ant release -# Prompted for version input -``` - -**New (Gradle):** -```bash -# Interactive - shows available versions -gradle release - -# Non-interactive - builds specific version -gradle release -PbundleVersion=1.6.39 -``` - -## Configuration Changes - -### build.properties - -**No changes required** - The file format remains the same: - -```properties -bundle.name = memcached -bundle.release = 2025.8.20 -bundle.type = bins -bundle.format = 7z -``` - -### gradle.properties - -**Enhanced** with performance optimizations: - -```properties -# Old (minimal) -# (empty or basic settings) - -# New (optimized) -org.gradle.daemon=true -org.gradle.parallel=true -org.gradle.caching=true -org.gradle.configureondemand=true -org.gradle.jvmargs=-Xmx2048m -Xms512m -XX:MaxMetaspaceSize=512m -org.gradle.console=rich -org.gradle.warning.mode=all -org.gradle.vfs.watch=true -``` - -### releases.properties - -**No changes required** - The file format remains the same. - -## Benefits - -### Performance - -| Benefit | Impact | -|------------------------------|--------------------------------------------------| -| Native Gradle tasks | Faster execution | -| Build caching | Reuse previous build outputs | -| Parallel execution | Better multi-core utilization | -| Daemon mode | Reduced startup time | -| Incremental builds | Only rebuild what changed | - -### Maintainability - -| Benefit | Impact | -|------------------------------|--------------------------------------------------| -| Pure Gradle | Simpler codebase | -| Self-contained | No external dependencies | -| Better documentation | Easier to understand and modify | -| Modern tooling | Better IDE support | - -### User Experience - -| Benefit | Impact | -|------------------------------|--------------------------------------------------| -| Better output formatting | Clearer feedback | -| More informative messages | Easier troubleshooting | -| Comprehensive help | Better discoverability | -| Verification tasks | Catch issues early | - -## Migration Steps - -### For Users - -If you're using the build system, follow these steps: - -#### Step 1: Update Repository - -```bash -# Pull latest changes -git pull origin main - -# Verify files -dir -``` - -**Expected files:** -- `build.gradle` (updated) -- `settings.gradle` (updated) -- `gradle.properties` (updated) -- `.gradle-docs/` (new) -- `build.xml` (removed) - -#### Step 2: Verify Environment - -```bash -# Verify build environment -gradle verify -``` - -**Expected output:** -``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Build Environment Verification ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -Environment Check Results: -──────────────────────────────────────────────────────────────────────────── - ✓ PASS Java 8+ - ✓ PASS build.gradle - ✓ PASS build.properties - ✓ PASS releases.properties - ✓ PASS settings.gradle - ✓ PASS bin/ directory - ✓ PASS 7z command -──────────────────────────────────────────────────────────────────────────── - -[SUCCESS] All checks passed! Build environment is ready. -``` - -#### Step 3: Update Commands - -Replace old Ant commands with new Gradle commands: - -**Old:** -```bash -ant release -Dinput.bundle=1.6.39 -``` - -**New:** -```bash -gradle release -PbundleVersion=1.6.39 -``` - -#### Step 4: Learn New Features - -Explore new tasks: - -```bash -# Display build information -gradle info - -# List available versions -gradle listVersions - -# List all releases -gradle listReleases - -# List all tasks -gradle tasks -``` - -#### Step 5: Read Documentation - -Read the new documentation: - -```bash -# Open documentation -start .gradle-docs/README.md -``` - -Or browse online: -- [README.md](.gradle-docs/README.md) -- [TASKS.md](.gradle-docs/TASKS.md) -- [CONFIGURATION.md](.gradle-docs/CONFIGURATION.md) -- [RELEASE-PROCESS.md](.gradle-docs/RELEASE-PROCESS.md) - -### For Developers - -If you're modifying the build system: - -#### Step 1: Understand New Structure - -Review the new build structure: - -``` -module-memcached/ -├── build.gradle # Pure Gradle build script -├── settings.gradle # Gradle settings -├── gradle.properties # Gradle configuration -├── build.properties # Bundle configuration -├── releases.properties # Release history -└── .gradle-docs/ # Documentation - ├── README.md - ├── TASKS.md - ├── CONFIGURATION.md - ├── RELEASE-PROCESS.md - ├── MIGRATION-GUIDE.md - └── INDEX.md -``` - -#### Step 2: Review Build Script - -Study the new `build.gradle`: - -- Pure Gradle implementation -- No Ant imports -- Native task definitions -- Enhanced output formatting -- Comprehensive error handling - -#### Step 3: Test Changes - -Test your changes: - -```bash -# Verify environment -gradle verify - -# Test build -gradle release -PbundleVersion=1.6.39 - -# Clean up -gradle clean -``` - -#### Step 4: Update Documentation - -Update documentation if needed: - -- Update relevant `.gradle-docs/*.md` files -- Update version and date -- Test all examples -- Update index if structure changes - -### For CI/CD - -If you're using CI/CD pipelines: - -#### Step 1: Update Pipeline Scripts - -**Old (Ant):** -```yaml -- name: Build Release - run: ant release -Dinput.bundle=1.6.39 -``` - -**New (Gradle):** -```yaml -- name: Build Release - run: gradle release -PbundleVersion=1.6.39 -``` - -#### Step 2: Update Environment - -Ensure CI/CD environment has: - -- Java 8+ -- Gradle 7.0+ -- 7-Zip (for 7z format) - -#### Step 3: Update Caching - -Leverage Gradle caching: - -```yaml -- name: Cache Gradle - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} -``` - -#### Step 4: Test Pipeline - -Test the updated pipeline: - -```bash -# Run pipeline locally (if possible) -# Or trigger test build on CI/CD -``` - -## Troubleshooting - -### Common Issues - -#### Issue: build.xml not found - -**Error:** -``` -build.xml not found -``` - -**Cause:** Trying to use old Ant commands - -**Solution:** Use new Gradle commands: -```bash -# Old -ant release -Dinput.bundle=1.6.39 - -# New -gradle release -PbundleVersion=1.6.39 -``` - ---- - -#### Issue: Task not found - -**Error:** -``` -Task 'ant-release' not found -``` - -**Cause:** Trying to use old Ant task names - -**Solution:** Use new Gradle task names: -```bash -# List available tasks -gradle tasks - -# Use correct task name -gradle release -PbundleVersion=1.6.39 -``` - ---- - -#### Issue: Property not recognized - -**Error:** -``` -Property 'input.bundle' not recognized -``` - -**Cause:** Using old Ant property names - -**Solution:** Use new Gradle property names: -```bash -# Old --Dinput.bundle=1.6.39 - -# New --PbundleVersion=1.6.39 -``` - ---- - -#### Issue: Build fails with "dev path not found" - -**Error:** -``` -Dev path not found: E:/Bearsampp-development/dev -``` - -**Cause:** Old build script looking for external Ant files - -**Solution:** Update to latest build.gradle: -```bash -git pull origin main -``` - ---- - -### Getting Help - -If you encounter issues: - -1. **Read Documentation** - - [README.md](.gradle-docs/README.md) - - [TASKS.md](.gradle-docs/TASKS.md) - - [CONFIGURATION.md](.gradle-docs/CONFIGURATION.md) - -2. **Check Troubleshooting** - - [README.md - Troubleshooting](.gradle-docs/README.md#troubleshooting) - - [CONFIGURATION.md - Troubleshooting](.gradle-docs/CONFIGURATION.md#troubleshooting-configuration) - - [RELEASE-PROCESS.md - Troubleshooting](.gradle-docs/RELEASE-PROCESS.md#troubleshooting) - -3. **Verify Environment** - ```bash - gradle verify - ``` - -4. **Run with Debug** - ```bash - gradle release -PbundleVersion=1.6.39 --info - gradle release -PbundleVersion=1.6.39 --debug - ``` - -5. **Report Issues** - - [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) - -## Comparison - -### Side-by-Side Comparison - -#### Building a Release - -**Old (Ant):** -```bash -# Interactive only -ant release -# Enter version when prompted: 1.6.39 - -# Or with property -ant release -Dinput.bundle=1.6.39 -``` - -**New (Gradle):** -```bash -# Interactive - shows available versions -gradle release - -# Non-interactive - builds specific version -gradle release -PbundleVersion=1.6.39 -``` - -#### Listing Information - -**Old (Ant):** -```bash -# No built-in commands -# Manual inspection required -dir bin -type releases.properties -``` - -**New (Gradle):** -```bash -# List available versions -gradle listVersions - -# List all releases -gradle listReleases - -# Display build info -gradle info -``` - -#### Verification - -**Old (Ant):** -```bash -# No built-in verification -# Manual checks required -``` - -**New (Gradle):** -```bash -# Verify environment -gradle verify - -# Validate properties -gradle validateProperties -``` - -### Feature Comparison - -| Feature | Ant/Gradle Hybrid | Pure Gradle | -|------------------------------|------------------------|------------------------| -| Build Speed | Moderate | Fast | -| Caching | Limited | Full | -| Parallel Execution | No | Yes | -| Incremental Builds | No | Yes | -| Self-Contained | No | Yes | -| Documentation | Basic | Comprehensive | -| Verification | No | Yes | -| Output Formatting | Basic | Enhanced | -| Error Messages | Basic | Detailed | -| IDE Support | Limited | Full | - -## Best Practices - -### After Migration - -1. **Update Bookmarks** - - Update any documentation bookmarks - - Update command references - - Update CI/CD scripts - -2. **Train Team** - - Share new documentation - - Demonstrate new commands - - Explain benefits - -3. **Monitor** - - Watch for issues - - Gather feedback - - Improve documentation - -4. **Optimize** - - Tune Gradle settings - - Leverage caching - - Use parallel execution - -## Rollback - -If you need to rollback to the old system: - -```bash -# Checkout previous version -git checkout - -# Or revert changes -git revert -``` - -**Note:** Rollback is not recommended as the new system provides significant improvements. - -## Feedback - -We welcome feedback on the migration: - -- Report issues: [GitHub Issues](https://github.com/bearsampp/bearsampp/issues) -- Suggest improvements: [GitHub Discussions](https://github.com/bearsampp/bearsampp/discussions) -- Contribute: [Contributing Guide](../README.md#contributing) - ---- - -**Last Updated:** 2025-08-20 -**Version:** 2025.8.20 -**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/MIGRATION.md b/.gradle-docs/MIGRATION.md new file mode 100644 index 0000000..1a1412b --- /dev/null +++ b/.gradle-docs/MIGRATION.md @@ -0,0 +1,511 @@ +# Migration Guide: Ant to Gradle + +Complete guide for migrating from the legacy Ant build system to the modern Gradle build system. + +--- + +## Table of Contents + +- [Overview](#overview) +- [What Changed](#what-changed) +- [Command Mapping](#command-mapping) +- [File Changes](#file-changes) +- [Configuration Changes](#configuration-changes) +- [Task Equivalents](#task-equivalents) +- [Benefits of Migration](#benefits-of-migration) +- [Troubleshooting](#troubleshooting) +- [Next Steps](#next-steps) + +--- + +## Overview + +The Bearsampp Module Memcached project has been fully migrated from Apache Ant to Gradle. This migration provides: + +- **Modern Build System**: Native Gradle features and conventions +- **Better Performance**: Incremental builds, caching, and parallel execution +- **Simplified Maintenance**: Pure Groovy/Gradle DSL instead of XML +- **Enhanced Tooling**: Better IDE integration and dependency management +- **Cross-Platform Support**: Consistent behavior across Windows, Linux, and macOS + +### Migration Status + +| Component | Status | Notes | +|-------------------|-------------|------------------------------------------| +| Build System | ✅ Complete | Fully migrated to Gradle | +| Build Scripts | ✅ Complete | build.xml removed, build.gradle created | +| Configuration | ✅ Complete | build.properties retained and enhanced | +| Tasks | ✅ Complete | All Ant targets converted to Gradle tasks| +| Documentation | ✅ Complete | Updated for Gradle | + +--- + +## What Changed + +### Removed Components + +| Component | Status | Replacement | +|-------------------|-------------|------------------------------------------| +| `build.xml` | ❌ Removed | `build.gradle` | +| Ant tasks | ❌ Removed | Gradle tasks | +| Ant properties | ❌ Removed | Gradle properties and ext variables | + +### New Components + +| Component | Status | Purpose | +|-------------------|-------------|------------------------------------------| +| `build.gradle` | ✅ Added | Main Gradle build script | +| `settings.gradle` | ✅ Added | Gradle project settings | +| `gradle.properties` | ✅ Added | Gradle-specific configuration | +| `.gradle-docs/` | ✅ Added | Comprehensive Gradle documentation | + +### Retained Components + +| Component | Status | Notes | +|-------------------|-------------|------------------------------------------| +| `build.properties`| ✅ Retained | Enhanced with new options | +| `releases.properties` | ✅ Retained | Release history tracking | +| `bin/` directory | ✅ Retained | Memcached version bundles | + +--- + +## Command Mapping + +### Build Commands + +| Ant Command | Gradle Command | Notes | +|--------------------------------------|---------------------------------------------|-------| +| `ant release` | `gradle release` | Interactive mode | +| `ant release -Dinput.bundle=1.6.29` | `gradle release -PbundleVersion=1.6.29` | Non-interactive mode | +| `ant clean` | `gradle clean` | Clean build artifacts | +| N/A | `gradle releaseAll` | New: Build all versions | + +### Information Commands + +| Ant Command | Gradle Command | Notes | +|--------------------------------------|---------------------------------------------|-------| +| N/A | `gradle info` | New: Display build info | +| N/A | `gradle tasks` | New: List all tasks | +| N/A | `gradle listVersions` | New: List local versions | +| N/A | `gradle listReleases` | New: List remote versions | + +### Verification Commands + +| Ant Command | Gradle Command | Notes | +|--------------------------------------|---------------------------------------------|-------| +| N/A | `gradle verify` | New: Verify environment | +| N/A | `gradle validateProperties` | New: Validate config | +| N/A | `gradle checkModulesUntouched` | New: Check integration | + +--- + +## File Changes + +### Build Scripts + +#### Before (Ant) + +**File**: `build.xml` + +```xml + + + + + + + + + + + + +``` + +#### After (Gradle) + +**File**: `build.gradle` + +```groovy +plugins { + id 'base' +} + +// Load build properties +def buildProps = new Properties() +file('build.properties').withInputStream { buildProps.load(it) } + +// Project information +group = 'com.bearsampp.modules' +version = buildProps.getProperty('bundle.release', '1.0.0') + +// Tasks +tasks.register('release') { + group = 'build' + description = 'Build release package' + // Gradle build logic +} + +tasks.named('clean') { + group = 'build' + description = 'Clean build artifacts' + // Gradle clean logic +} +``` + +### Configuration Files + +#### build.properties + +**Before (Ant)**: +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +**After (Gradle)**: +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z + +# Optional: Custom build path +# build.path = C:/Bearsampp-build +``` + +**Changes**: +- ✅ All existing properties retained +- ✅ New optional `build.path` property added +- ✅ Better documentation and comments + +--- + +## Configuration Changes + +### Property Access + +#### Before (Ant) + +```xml + + +``` + +#### After (Gradle) + +```groovy +def buildProps = new Properties() +file('build.properties').withInputStream { buildProps.load(it) } + +ext { + bundleName = buildProps.getProperty('bundle.name', 'memcached') +} + +println "Building ${bundleName}" +``` + +### Path Configuration + +#### Before (Ant) + +```xml + +``` + +#### After (Gradle) + +```groovy +// Priority: 1) build.properties, 2) Environment variable, 3) Default +def buildPathFromProps = buildProps.getProperty('build.path', '').trim() +def buildPathFromEnv = System.getenv('BEARSAMPP_BUILD_PATH') ?: '' +def defaultBuildPath = "${rootDir}/bearsampp-build" + +buildBasePath = buildPathFromProps ?: (buildPathFromEnv ?: defaultBuildPath) +``` + +**Improvements**: +- ✅ Multiple configuration sources +- ✅ Environment variable support +- ✅ Sensible defaults +- ✅ Clear priority order + +--- + +## Task Equivalents + +### Release Task + +#### Before (Ant) + +```xml + + + + + + + + + +``` + +#### After (Gradle) + +```groovy +tasks.register('release') { + group = 'build' + description = 'Build release package' + + doLast { + def versionToBuild = project.findProperty('bundleVersion') + + if (!versionToBuild) { + // Interactive mode with version selection + def availableVersions = getAvailableVersions() + // ... prompt logic ... + } + + // Copy files + copy { + from bundleSrcDest + into memcachedPrepPath + } + + // Create archive with hash files + // ... archive creation logic ... + generateHashFiles(archiveFile) + } +} +``` + +**Improvements**: +- ✅ Better interactive mode with version listing +- ✅ Non-interactive mode support +- ✅ Automatic hash file generation +- ✅ Better error handling +- ✅ Progress reporting + +### Clean Task + +#### Before (Ant) + +```xml + + + +``` + +#### After (Gradle) + +```groovy +tasks.named('clean') { + group = 'build' + description = 'Clean build artifacts' + + doLast { + def buildDir = file("${projectDir}/build") + if (buildDir.exists()) { + delete buildDir + } + println "[SUCCESS] Build artifacts cleaned" + } +} +``` + +**Improvements**: +- ✅ Uses Gradle's built-in clean task +- ✅ Better feedback +- ✅ Safer deletion logic + +--- + +## Benefits of Migration + +### Performance + +| Feature | Ant | Gradle | Improvement | +|----------------------|--------------|--------------|-------------| +| Incremental Builds | ❌ No | ✅ Yes | Faster rebuilds | +| Build Caching | ❌ No | ✅ Yes | Reuse previous builds | +| Parallel Execution | ❌ No | ✅ Yes | Faster multi-task builds | +| Daemon Mode | ❌ No | ✅ Yes | Faster startup | + +### Developer Experience + +| Feature | Ant | Gradle | Improvement | +|----------------------|--------------|--------------|-------------| +| IDE Integration | ⚠️ Limited | ✅ Excellent | Better tooling | +| Task Discovery | ⚠️ Manual | ✅ Built-in | `gradle tasks` | +| Dependency Management| ❌ Manual | ✅ Automatic | Easier updates | +| Error Messages | ⚠️ Basic | ✅ Detailed | Better debugging | + +### Maintainability + +| Feature | Ant | Gradle | Improvement | +|----------------------|--------------|--------------|-------------| +| Build Script Language| XML | Groovy DSL | More readable | +| Code Reuse | ⚠️ Limited | ✅ Excellent | Functions, plugins | +| Testing | ❌ Difficult | ✅ Easy | Built-in support | +| Documentation | ⚠️ External | ✅ Integrated| Task descriptions | + +### Features + +| Feature | Ant | Gradle | Notes | +|----------------------|--------------|--------------|-------| +| Interactive Mode | ⚠️ Basic | ✅ Enhanced | Version selection | +| Batch Building | ❌ No | ✅ Yes | `releaseAll` task | +| Hash Generation | ❌ Manual | ✅ Automatic | MD5, SHA1, SHA256, SHA512 | +| Environment Checks | ❌ No | ✅ Yes | `verify` task | +| Version Listing | ❌ No | ✅ Yes | `listVersions` task | + +--- + +## Troubleshooting + +### Common Migration Issues + +#### Issue: "build.xml not found" + +**Symptom**: Old scripts or documentation reference `build.xml` + +**Solution**: +- `build.xml` has been removed +- Use `build.gradle` instead +- Update any scripts or documentation + +#### Issue: "Ant command not working" + +**Symptom**: `ant release` fails + +**Solution**: +- Ant is no longer used +- Use Gradle commands instead +- See [Command Mapping](#command-mapping) + +#### Issue: "Properties not found" + +**Symptom**: Build fails with missing properties + +**Solution**: +1. Verify `build.properties` exists +2. Run `gradle validateProperties` +3. Check property names match exactly + +#### Issue: "Different output location" + +**Symptom**: Archives not in expected location + +**Solution**: +- Gradle uses `bearsampp-build/` structure +- Configure with `build.path` in build.properties +- Or set `BEARSAMPP_BUILD_PATH` environment variable + +--- + +## Next Steps + +### For Developers + +1. **Install Gradle**: Download from https://gradle.org/ +2. **Verify Environment**: Run `gradle verify` +3. **Learn Commands**: Review [Command Mapping](#command-mapping) +4. **Read Documentation**: See [.gradle-docs/README.md](README.md) +5. **Try Building**: Run `gradle release` + +### For CI/CD + +1. **Update Scripts**: Replace Ant commands with Gradle +2. **Install Gradle**: Add to CI environment +3. **Use Non-Interactive Mode**: Always specify `-PbundleVersion` +4. **Cache Gradle**: Cache `.gradle` directory for faster builds +5. **Test Builds**: Verify all versions build correctly + +### For Contributors + +1. **Read Build Script**: Review `build.gradle` +2. **Understand Tasks**: See [TASKS.md](TASKS.md) +3. **Learn Configuration**: See [CONFIGURATION.md](CONFIGURATION.md) +4. **Follow Conventions**: Use Gradle best practices +5. **Update Documentation**: Keep docs in sync with changes + +--- + +## Migration Checklist + +Use this checklist to ensure complete migration: + +### Build System + +- [x] Remove `build.xml` +- [x] Create `build.gradle` +- [x] Create `settings.gradle` +- [x] Create `gradle.properties` +- [x] Test all build tasks + +### Configuration + +- [x] Verify `build.properties` works +- [x] Add optional properties +- [x] Test environment variables +- [x] Validate configuration + +### Documentation + +- [x] Update README.md +- [x] Create .gradle-docs/ +- [x] Write task documentation +- [x] Write configuration guide +- [x] Write migration guide + +### Testing + +- [x] Test release task +- [x] Test releaseAll task +- [x] Test clean task +- [x] Test verification tasks +- [x] Test information tasks + +### Cleanup + +- [x] Remove Ant references +- [x] Update scripts +- [x] Update CI/CD +- [x] Archive old documentation + +--- + +## Support + +For migration assistance: + +- **Documentation**: [README.md](README.md) +- **Tasks Reference**: [TASKS.md](TASKS.md) +- **Configuration**: [CONFIGURATION.md](CONFIGURATION.md) +- **GitHub Issues**: https://github.com/bearsampp/module-memcached/issues +- **Bearsampp Issues**: https://github.com/bearsampp/bearsampp/issues + +--- + +## Additional Resources + +### Gradle + +- [Gradle Documentation](https://docs.gradle.org/) +- [Gradle User Manual](https://docs.gradle.org/current/userguide/userguide.html) +- [Gradle Build Language Reference](https://docs.gradle.org/current/dsl/) +- [Migrating from Ant](https://docs.gradle.org/current/userguide/migrating_from_ant.html) + +### Bearsampp + +- [Bearsampp Project](https://github.com/bearsampp/bearsampp) +- [Bearsampp Website](https://bearsampp.com) +- [Bearsampp Documentation](https://bearsampp.com/docs) + +--- + +**Last Updated**: 2025-08-20 +**Version**: 2025.8.20 +**Migration Status**: Complete diff --git a/.gradle-docs/MIGRATION_SUMMARY.md b/.gradle-docs/MIGRATION_SUMMARY.md deleted file mode 100644 index bfe47b2..0000000 --- a/.gradle-docs/MIGRATION_SUMMARY.md +++ /dev/null @@ -1,248 +0,0 @@ -# Migration Summary: Ant to Modern Gradle Build - -## Overview - -The Bearsampp Memcached module has been migrated from a hybrid Ant/Gradle build system to a modern, pure Gradle build system based on the module-bruno implementation. - -## Changes Made - -### 1. Build System Architecture - -**Before:** -- Hybrid Ant/Gradle system -- Dependency on external Ant build files -- Required `build.xml`, `build-commons.xml`, `build-bundle.xml` -- Ant tasks imported with `ant-` prefix - -**After:** -- Pure Gradle implementation -- No Ant dependencies -- Self-contained build logic -- Native Gradle tasks - -### 2. Build Script (build.gradle) - -**Key Improvements:** -- Removed Ant integration code -- Added native Gradle task implementations -- Implemented helper functions for common operations -- Added modules-untouched integration -- Implemented automatic hash file generation -- Added comprehensive error handling - -**New Helper Functions:** -- `fetchModulesUntouchedProperties()` - Fetch version info from remote repository -- `find7ZipExecutable()` - Locate 7-Zip installation -- `generateHashFiles()` - Create MD5, SHA1, SHA256, SHA512 hash files -- `calculateHash()` - Calculate file hashes -- `getAvailableVersions()` - List available versions from bin/ and bin/archived/ - -### 3. Task Changes - -**Build Tasks:** -- `release` - Completely rewritten for native Gradle - - No longer calls Ant - - Direct file operations - - Integrated hash generation - - Better error messages -- `releaseAll` - NEW: Build all available versions -- `clean` - Simplified, pure Gradle implementation - -**Help Tasks:** -- `info` - Enhanced with more details -- `listVersions` - Enhanced to show bin/ and bin/archived/ -- `listReleases` - NEW: List releases from modules-untouched - -**Verification Tasks:** -- `verify` - Updated checks, removed Ant dependencies -- `validateProperties` - Unchanged -- `checkModulesUntouched` - NEW: Test remote integration - -### 4. Features Added - -1. **Automatic Hash Generation** - - MD5, SHA1, SHA256, SHA512 files created automatically - - No manual hash creation needed - -2. **Modules-Untouched Integration** - - Fetches version information from remote repository - - Graceful fallback if remote unavailable - - Version discovery and listing - -3. **Batch Building** - - `releaseAll` task builds all versions - - Progress tracking - - Success/failure summary - -4. **Enhanced Version Management** - - Support for bin/archived/ directory - - Automatic version discovery - - Better version listing - -5. **Improved Error Handling** - - Detailed error messages - - Helpful suggestions - - Graceful degradation - -### 5. Documentation - -**New Documentation Files:** -- `.gradle-docs/README.md` - Documentation overview -- `.gradle-docs/QUICK_REFERENCE.md` - Quick command reference -- `.gradle-docs/FEATURE_SUMMARY.md` - Feature overview -- `.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md` - Integration details -- `.gradle-docs/CONFIGURATION_SUMMARY.md` - Configuration guide -- `.gradle-docs/MIGRATION_SUMMARY.md` - This file - -**Updated Files:** -- `README.md` - Added comprehensive build documentation - -### 6. Removed Dependencies - -**No Longer Required:** -- `build.xml` - Ant build file (can be removed) -- `build-commons.xml` - Common Ant tasks (external) -- `build-bundle.xml` - Bundle Ant tasks (external) -- Ant runtime - -**Still Required:** -- `build.properties` - Configuration file -- Java 8+ -- Gradle -- 7-Zip (for .7z format) - -## Migration Benefits - -### 1. Simplified Maintenance -- Single build system (Gradle only) -- No Ant knowledge required -- Easier to understand and modify - -### 2. Modern Features -- Gradle caching -- Incremental builds -- Better dependency management -- Native task execution - -### 3. Better User Experience -- Clearer error messages -- More informative output -- Better progress tracking -- Comprehensive help - -### 4. Enhanced Automation -- Automatic hash generation -- Batch building -- Remote version fetching -- Better validation - -### 5. Improved Reliability -- Better error handling -- Graceful fallbacks -- Comprehensive verification -- Consistent behavior - -## Backward Compatibility - -### Breaking Changes - -1. **Command Syntax** - - Old: `gradle release` (interactive) - - New: `gradle release -PbundleVersion=X.X.X` (explicit version required) - -2. **Ant Tasks Removed** - - All `ant-*` prefixed tasks removed - - Use native Gradle tasks instead - -3. **Build Output** - - Hash files now generated automatically - - Output structure unchanged - -### Migration Path - -For existing workflows: - -1. **Update Build Commands** - ```bash - # Old - gradle release - # (then enter version when prompted) - - # New - gradle release -PbundleVersion=1.6.29 - ``` - -2. **Update Scripts** - - Replace Ant task calls with Gradle equivalents - - Update version specification to use `-PbundleVersion` - -3. **Verify Environment** - ```bash - gradle verify - ``` - -## Testing - -All functionality has been tested and verified: - -✅ `gradle info` - Displays build information -✅ `gradle verify` - Verifies build environment -✅ `gradle listVersions` - Lists available versions -✅ `gradle listReleases` - Lists remote releases -✅ `gradle checkModulesUntouched` - Tests remote integration -✅ `gradle validateProperties` - Validates configuration -✅ Task grouping and organization -✅ Error handling and messages - -## Next Steps - -### Recommended Actions - -1. **Test Build Process** - ```bash - gradle release -PbundleVersion=1.6.29 - ``` - -2. **Update CI/CD** - - Update build scripts to use new command syntax - - Remove Ant dependencies - - Update documentation - -3. **Clean Up (Optional)** - - Remove `build.xml` if no longer needed - - Update `.gitignore` if necessary - - Archive old build scripts - -4. **Team Communication** - - Notify team of changes - - Share new documentation - - Provide training if needed - -### Future Enhancements - -Potential improvements: -- Parallel version building -- Custom compression levels -- Additional archive formats -- Build caching optimization -- Release notes generation -- Automated testing integration - -## Support - -For questions or issues: -- Review documentation in `.gradle-docs/` -- Check [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues) -- Run `gradle tasks` for available commands -- Run `gradle help --task ` for task details - -## Conclusion - -The migration to a modern Gradle build system provides: -- Simplified maintenance -- Better features -- Improved reliability -- Enhanced automation -- Better user experience - -The build system is now consistent with module-bruno and provides a solid foundation for future enhancements. diff --git a/.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md b/.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md deleted file mode 100644 index 3dc1315..0000000 --- a/.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md +++ /dev/null @@ -1,221 +0,0 @@ -# Modules-Untouched Integration - -## Overview - -The build system integrates with the [Bearsampp modules-untouched repository](https://github.com/Bearsampp/modules-untouched) to fetch version information and download URLs for Memcached releases. - -## How It Works - -### 1. Remote Properties File - -The system fetches version information from: -``` -https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties -``` - -This file contains mappings of version numbers to download URLs: -```properties -1.6.15 = https://example.com/memcached-1.6.15.zip -1.6.17 = https://example.com/memcached-1.6.17.zip -... -``` - -### 2. Version Resolution Strategy - -The build system uses a two-tier strategy: - -1. **Primary**: Fetch from modules-untouched repository - - Attempts to download `memcached.properties` from GitHub - - Parses version-to-URL mappings - - Uses these URLs for downloads - -2. **Fallback**: Standard URL construction - - If remote fetch fails (network issues, file not found, etc.) - - Constructs URLs using standard format - - Continues build process without interruption - -### 3. Implementation - -The integration is implemented in the `fetchModulesUntouchedProperties()` helper function: - -```groovy -def fetchModulesUntouchedProperties() { - def propsUrl = "https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties" - try { - def connection = new URL(propsUrl).openConnection() - connection.setRequestProperty("User-Agent", "Bearsampp-Build") - connection.setConnectTimeout(5000) - connection.setReadTimeout(5000) - - def props = new Properties() - connection.inputStream.withCloseable { stream -> - props.load(stream) - } - return props - } catch (Exception e) { - println "Warning: Could not fetch modules-untouched properties: ${e.message}" - return null - } -} -``` - -## Available Commands - -### Check Integration Status - -```bash -gradle checkModulesUntouched -``` - -This command: -- Tests connectivity to modules-untouched repository -- Displays all available versions -- Shows version resolution strategy -- Reports success or failure with details - -### List Remote Releases - -```bash -gradle listReleases -``` - -This command: -- Fetches the properties file from modules-untouched -- Lists all available versions and their URLs -- Shows total number of releases - -## Benefits - -### 1. Centralized Version Management - -- Single source of truth for version information -- Easy to add new versions without modifying build scripts -- Consistent across all Bearsampp modules - -### 2. Flexible URL Management - -- URLs can be updated without changing build scripts -- Support for different download sources -- Easy migration to new hosting - -### 3. Graceful Degradation - -- Build continues even if remote fetch fails -- No hard dependency on network connectivity -- Fallback ensures builds always work - -### 4. Version Discovery - -- Automatically discover new versions -- No manual version list maintenance -- Easy to see what's available - -## Error Handling - -### Network Failures - -If the remote fetch fails: -``` -Warning: Could not fetch modules-untouched properties: Connection timeout -``` - -The build continues using fallback URL construction. - -### File Not Found - -If the properties file doesn't exist: -``` -Warning: Could not fetch modules-untouched properties: 404 Not Found -``` - -The build continues with fallback strategy. - -### Parse Errors - -If the properties file is malformed: -``` -Warning: Could not fetch modules-untouched properties: Invalid properties format -``` - -The build continues with fallback strategy. - -## Configuration - -### Timeout Settings - -The integration uses conservative timeout settings: -- **Connect Timeout**: 5 seconds -- **Read Timeout**: 5 seconds - -These can be adjusted in the `fetchModulesUntouchedProperties()` function if needed. - -### User Agent - -The system identifies itself as `Bearsampp-Build` in HTTP requests. - -## Testing - -### Test Integration - -```bash -gradle checkModulesUntouched -``` - -Expected output: -``` -====================================================================== -Modules-Untouched Integration Check -====================================================================== - -Repository URL: - https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties - -Fetching memcached.properties from modules-untouched... - -====================================================================== -Available Versions in modules-untouched -====================================================================== - 1.6.15 - 1.6.17 - ... -====================================================================== -Total versions: 12 - -====================================================================== -[SUCCESS] modules-untouched integration is working -====================================================================== -``` - -### Test Fallback - -To test the fallback mechanism: -1. Disconnect from the internet -2. Run `gradle checkModulesUntouched` -3. Observe the warning message and fallback behavior - -## Maintenance - -### Adding New Versions - -To add a new Memcached version: - -1. Update the `memcached.properties` file in modules-untouched repository -2. Add the version-to-URL mapping -3. Commit and push changes -4. The build system will automatically pick up the new version - -### Updating URLs - -To update download URLs: - -1. Edit the `memcached.properties` file in modules-untouched repository -2. Update the URL for the specific version -3. Commit and push changes -4. No build script changes needed - -## Related Tasks - -- `gradle listReleases` - List all remote releases -- `gradle checkModulesUntouched` - Test integration -- `gradle listVersions` - List local versions -- `gradle verify` - Verify build environment diff --git a/.gradle-docs/QUICK_REFERENCE.md b/.gradle-docs/QUICK_REFERENCE.md deleted file mode 100644 index 36fb278..0000000 --- a/.gradle-docs/QUICK_REFERENCE.md +++ /dev/null @@ -1,99 +0,0 @@ -# Quick Reference Guide - -## Common Commands - -### Build Commands - -```bash -# Build a specific version (non-interactive) -gradle release -PbundleVersion=1.6.29 - -# Build interactively (prompts for version selection) -gradle release - -# Build all available versions -gradle releaseAll - -# Clean build artifacts -gradle clean -``` - -### Information Commands - -```bash -# Display build information -gradle info - -# List all available tasks -gradle tasks - -# List versions in bin/ directory -gradle listVersions - -# List releases from modules-untouched -gradle listReleases -``` - -### Verification Commands - -```bash -# Verify build environment -gradle verify - -# Validate build.properties -gradle validateProperties - -# Check modules-untouched integration -gradle checkModulesUntouched -``` - -## Build Output - -Built releases are placed in: -- **Build directory**: `C:/Bearsampp-build/memcachedX.X.X/` -- **Archive format**: `.7z` (configurable in build.properties) -- **Hash files**: `.md5`, `.sha1`, `.sha256`, `.sha512` - -## Configuration - -Edit `build.properties` to configure: - -```properties -bundle.name = memcached -bundle.release = 2025.8.20 -bundle.type = bins -bundle.format = 7z -#build.path = C:/Bearsampp-build -``` - -## Troubleshooting - -### 7-Zip not found - -If you get a "7-Zip executable not found" error: - -1. Install 7-Zip from https://www.7-zip.org/ -2. Or set the `7Z_HOME` environment variable to your 7-Zip installation directory - -### Version not found - -If a version is not found: - -1. Run `gradle listVersions` to see available versions -2. Ensure the version exists in `bin/` or `bin/archived/` directory -3. Check the version format matches `memcachedX.X.X` - -### Network issues - -If modules-untouched integration fails: - -1. Check your internet connection -2. The build will continue using fallback URL construction -3. Run `gradle checkModulesUntouched` to test the connection - -## Tips - -- Use `gradle tasks --all` to see all available tasks including internal ones -- Use `gradle help --task ` to get detailed help for a specific task -- Enable Gradle daemon for faster builds: `gradle --daemon` -- Use `--info` or `--debug` flags for more detailed output diff --git a/.gradle-docs/README.md b/.gradle-docs/README.md index 1c0d4e9..863c866 100644 --- a/.gradle-docs/README.md +++ b/.gradle-docs/README.md @@ -1,81 +1,446 @@ # Bearsampp Module Memcached - Gradle Build Documentation -This directory contains documentation for the Gradle build system used in the Bearsampp Memcached module. +## Table of Contents -## Documentation Files +- [Overview](#overview) +- [Quick Start](#quick-start) +- [Installation](#installation) +- [Build Tasks](#build-tasks) +- [Configuration](#configuration) +- [Architecture](#architecture) +- [Troubleshooting](#troubleshooting) +- [Migration Guide](#migration-guide) -- **QUICK_REFERENCE.md** - Quick reference guide for common tasks -- **INTERACTIVE_MODE.md** - Interactive vs non-interactive build modes -- **FEATURE_SUMMARY.md** - Overview of build system features -- **MODULES_UNTOUCHED_INTEGRATION.md** - Integration with modules-untouched repository -- **CONFIGURATION_SUMMARY.md** - Build configuration details -- **MIGRATION_SUMMARY.md** - Migration from Ant to Gradle details +--- + +## Overview + +The Bearsampp Module Memcached project has been converted to a **pure Gradle build system**, replacing the legacy Ant build configuration. This provides: + +- **Modern Build System** - Native Gradle tasks and conventions +- **Better Performance** - Incremental builds and caching +- **Simplified Maintenance** - Pure Groovy/Gradle DSL +- **Enhanced Tooling** - IDE integration and dependency management +- **Cross-Platform Support** - Works on Windows, Linux, and macOS + +### Project Information + +| Property | Value | +|-------------------|------------------------------------------| +| **Project Name** | module-memcached | +| **Group** | com.bearsampp.modules | +| **Type** | Memcached Module Builder | +| **Build Tool** | Gradle 8.x+ | +| **Language** | Groovy (Gradle DSL) | + +--- ## Quick Start +### Prerequisites + +| Requirement | Version | Purpose | +|-------------------|---------------|------------------------------------------| +| **Java** | 8+ | Required for Gradle execution | +| **Gradle** | 8.0+ | Build automation tool | +| **7-Zip** | Latest | Archive creation (.7z format) | + +### Basic Commands + ```bash # Display build information gradle info +# List all available tasks +gradle tasks + # Verify build environment gradle verify -# List available versions -gradle listVersions +# Build a release (interactive) +gradle release -# Build a specific version +# Build a specific version (non-interactive) gradle release -PbundleVersion=1.6.29 # Build all versions gradle releaseAll + +# Clean build artifacts +gradle clean +``` + +--- + +## Installation + +### 1. Clone the Repository + +```bash +git clone https://github.com/bearsampp/module-memcached.git +cd module-memcached +``` + +### 2. Verify Environment + +```bash +gradle verify +``` + +This will check: +- Java version (8+) +- Required files (build.properties) +- Directory structure (bin/, bin/archived/) +- Build dependencies (7-Zip for .7z format) + +### 3. List Available Versions + +```bash +gradle listVersions +``` + +### 4. Build Your First Release + +```bash +# Interactive mode (prompts for version) +gradle release + +# Or specify version directly +gradle release -PbundleVersion=1.6.29 +``` + +--- + +## Build Tasks + +### Core Build Tasks + +| Task | Description | Example | +|-----------------------|--------------------------------------------------|------------------------------------------| +| `release` | Build and package release (interactive/non-interactive) | `gradle release -PbundleVersion=1.6.29` | +| `releaseAll` | Build releases for all available versions | `gradle releaseAll` | +| `clean` | Clean build artifacts and temporary files | `gradle clean` | + +### Verification Tasks + +| Task | Description | Example | +|---------------------------|----------------------------------------------|----------------------------------------------| +| `verify` | Verify build environment and dependencies | `gradle verify` | +| `validateProperties` | Validate build.properties configuration | `gradle validateProperties` | +| `checkModulesUntouched` | Check modules-untouched integration | `gradle checkModulesUntouched` | + +### Information Tasks + +| Task | Description | Example | +|---------------------|--------------------------------------------------|----------------------------| +| `info` | Display build configuration information | `gradle info` | +| `listVersions` | List available bundle versions in bin/ | `gradle listVersions` | +| `listReleases` | List all available releases from modules-untouched | `gradle listReleases` | + +### Task Groups + +| Group | Purpose | +|------------------|--------------------------------------------------| +| **build** | Build and package tasks | +| **verification** | Verification and validation tasks | +| **help** | Help and information tasks | + +--- + +## Configuration + +### build.properties + +The main configuration file for the build: + +```properties +bundle.name = memcached +bundle.release = 2025.8.20 +bundle.type = bins +bundle.format = 7z +``` + +| Property | Description | Example Value | +|-------------------|--------------------------------------|----------------| +| `bundle.name` | Name of the bundle | `memcached` | +| `bundle.release` | Release version | `2025.8.20` | +| `bundle.type` | Type of bundle | `bins` | +| `bundle.format` | Archive format (7z or zip) | `7z` | + +### gradle.properties + +Gradle-specific configuration: + +```properties +# Gradle daemon configuration +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true + +# JVM settings +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m +``` + +### Directory Structure + +``` +module-memcached/ +├── .gradle-docs/ # Gradle documentation +│ ├── README.md # Main documentation +│ ├── TASKS.md # Task reference +│ ├── CONFIGURATION.md # Configuration guide +│ └── MIGRATION.md # Migration guide +├── bin/ # Memcached version bundles +│ ├── memcached1.6.29/ +│ ├── memcached1.6.39/ +│ └── archived/ # Archived versions +│ └── memcached1.5.22/ +├── bearsampp-build/ # External build directory (outside repo) +│ ├── tmp/ # Temporary build files +│ │ ├── bundles_prep/bins/memcached/ # Prepared bundles +│ │ └── bundles_build/bins/memcached/ # Build staging +│ └── bins/memcached/ # Final packaged archives +│ └── 2025.8.20/ # Release version +│ ├── bearsampp-memcached-1.6.29-2025.8.20.7z +│ ├── bearsampp-memcached-1.6.29-2025.8.20.7z.md5 +│ ├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha1 +│ ├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha256 +│ └── bearsampp-memcached-1.6.29-2025.8.20.7z.sha512 +├── build.gradle # Main Gradle build script +├── settings.gradle # Gradle settings +├── build.properties # Build configuration +└── releases.properties # Release history +``` + +--- + +## Architecture + +### Build Process Flow + +``` +1. User runs: gradle release -PbundleVersion=1.6.29 + ↓ +2. Validate environment and version + ↓ +3. Create preparation directory (tmp/bundles_prep/) + ↓ +4. Copy base Memcached files from bin/memcached1.6.29/ + ↓ +5. Output prepared bundle to tmp/bundles_prep/ + ↓ +6. Copy to build staging (tmp/bundles_build/) + ↓ +7. Package prepared folder into archive + - Location: bearsampp-build/bins/memcached/{bundle.release}/ + - The archive includes the top-level folder: memcached{version}/ + ↓ +8. Generate hash files (MD5, SHA1, SHA256, SHA512) ``` -## Key Features +### Packaging Details -- **Native Gradle Build**: No Ant dependency, pure Gradle implementation -- **Hash File Generation**: Automatic MD5, SHA1, SHA256, SHA512 hash files -- **Modules-Untouched Integration**: Fetches version information from remote repository -- **Batch Building**: Build all available versions with a single command -- **Archive Support**: 7z and zip format support -- **Version Management**: Support for both bin/ and bin/archived/ directories +- **Archive name format**: `bearsampp-memcached-{version}-{bundle.release}.{7z|zip}` +- **Location**: `bearsampp-build/bins/memcached/{bundle.release}/` + - Example: `bearsampp-build/bins/memcached/2025.8.20/bearsampp-memcached-1.6.29-2025.8.20.7z` +- **Content root**: The top-level folder inside the archive is `memcached{version}/` (e.g., `memcached1.6.29/`) +- **Structure**: The archive contains the Memcached version folder at the root with all Memcached files inside -## Build System Architecture +**Archive Structure Example**: +``` +bearsampp-memcached-1.6.29-2025.8.20.7z +└── memcached1.6.29/ ← Version folder at root + ├── memcached.exe + ├── memcached.conf + └── ... +``` + +**Verification Commands**: + +```bash +# List archive contents with 7z +7z l bearsampp-build/bins/memcached/2025.8.20/bearsampp-memcached-1.6.29-2025.8.20.7z | more + +# You should see entries beginning with: +# memcached1.6.29/memcached.exe +# memcached1.6.29/memcached.conf +# memcached1.6.29/... + +# Extract and inspect with PowerShell (zip example) +Expand-Archive -Path bearsampp-build/bins/memcached/2025.8.20/bearsampp-memcached-1.6.29-2025.8.20.zip -DestinationPath .\_inspect +Get-ChildItem .\_inspect\memcached1.6.29 | Select-Object Name + +# Expected output: +# memcached.exe +# memcached.conf +# ... +``` + +**Note**: This archive structure matches the standard Bearsampp module pattern where archives contain `{module}{version}/` at the root. This ensures consistency across all Bearsampp modules. + +**Hash Files**: Each archive is accompanied by hash sidecar files: +- `.md5` - MD5 checksum +- `.sha1` - SHA-1 checksum +- `.sha256` - SHA-256 checksum +- `.sha512` - SHA-512 checksum + +Example: +``` +bearsampp-build/bins/memcached/2025.8.20/ +├── bearsampp-memcached-1.6.29-2025.8.20.7z +├── bearsampp-memcached-1.6.29-2025.8.20.7z.md5 +├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha1 +├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha256 +└── bearsampp-memcached-1.6.29-2025.8.20.7z.sha512 +``` + +### Modules-Untouched Integration + +The build system integrates with the [modules-untouched repository](https://github.com/Bearsampp/modules-untouched) to fetch version information: + +- **Repository**: `https://github.com/Bearsampp/modules-untouched` +- **Properties File**: `modules/memcached.properties` +- **Purpose**: Centralized version management and download URLs -The build system is organized into several key components: +The `listReleases` and `checkModulesUntouched` tasks query this repository to display available Memcached versions. -1. **Configuration** (`build.properties`) - - Bundle name, version, type, and format - - Build paths and output directories +--- -2. **Helper Functions** - - `fetchModulesUntouchedProperties()` - Fetch version info from remote - - `find7ZipExecutable()` - Locate 7-Zip installation - - `generateHashFiles()` - Create hash files for archives - - `calculateHash()` - Calculate file hashes - - `getAvailableVersions()` - List available versions +## Troubleshooting -3. **Build Tasks** - - `release` - Build single version - - `releaseAll` - Build all versions - - `clean` - Clean build artifacts +### Common Issues -4. **Verification Tasks** - - `verify` - Check build environment - - `validateProperties` - Validate configuration - - `checkModulesUntouched` - Test remote integration +#### Issue: "Dev path not found" -5. **Help Tasks** - - `info` - Display build information - - `listVersions` - List local versions - - `listReleases` - List remote versions +**Symptom:** +``` +Dev path not found: E:/Bearsampp-development/dev +``` + +**Solution:** +This is a warning only. The dev path is optional for most tasks. If you need it, ensure the `dev` project exists in the parent directory. + +--- + +#### Issue: "Bundle version not found" + +**Symptom:** +``` +Bundle version not found: E:/Bearsampp-development/module-memcached/bin/memcached1.6.99 +``` + +**Solution:** +1. List available versions: `gradle listVersions` +2. Use an existing version: `gradle release -PbundleVersion=1.6.29` -## Requirements +--- -- Java 8 or higher -- Gradle (wrapper included) -- 7-Zip (for .7z format archives) -- Internet connection (for modules-untouched integration) +#### Issue: "7-Zip executable not found" + +**Symptom:** +``` +7-Zip executable not found! +``` + +**Solution:** +1. Install 7-Zip from https://www.7-zip.org/ +2. Or set the `7Z_HOME` environment variable to your 7-Zip installation directory +3. Or use ZIP format instead by changing `bundle.format=zip` in build.properties + +--- + +#### Issue: "Java version too old" + +**Symptom:** +``` +Java 8+ required +``` + +**Solution:** +1. Check Java version: `java -version` +2. Install Java 8 or higher +3. Update JAVA_HOME environment variable + +--- + +### Debug Mode + +Run Gradle with debug output: + +```bash +gradle release -PbundleVersion=1.6.29 --info +gradle release -PbundleVersion=1.6.29 --debug +``` + +### Clean Build + +If you encounter issues, try a clean build: + +```bash +gradle clean +gradle release -PbundleVersion=1.6.29 +``` + +--- + +## Migration Guide + +### From Ant to Gradle + +The project has been fully migrated from Ant to Gradle. Here's what changed: + +#### Removed Files + +| File | Status | Replacement | +|-------------------|-----------|----------------------------| +| `build.xml` | ❌ Removed | `build.gradle` | + +#### Command Mapping + +| Ant Command | Gradle Command | +|--------------------------------------|---------------------------------------------| +| `ant release` | `gradle release` | +| `ant release -Dinput.bundle=1.6.29` | `gradle release -PbundleVersion=1.6.29` | +| `ant clean` | `gradle clean` | + +#### Key Differences + +| Aspect | Ant | Gradle | +|---------------------|------------------------------|----------------------------------| +| **Build File** | XML (build.xml) | Groovy DSL (build.gradle) | +| **Task Definition** | `` | `tasks.register('...')` | +| **Properties** | `` | `ext { ... }` | +| **Dependencies** | Manual downloads | Automatic with repositories | +| **Caching** | None | Built-in incremental builds | +| **IDE Support** | Limited | Excellent (IntelliJ, Eclipse) | + +For complete migration details, see [MIGRATION.md](MIGRATION.md). + +--- + +## Additional Resources + +- [Gradle Documentation](https://docs.gradle.org/) +- [Bearsampp Project](https://github.com/bearsampp/bearsampp) +- [Memcached Official](https://memcached.org/) +- [Memcached Downloads](https://memcached.org/downloads) + +--- ## Support -For issues and questions, please refer to the main [Bearsampp repository](https://github.com/bearsampp/bearsampp/issues). +For issues and questions: + +- **GitHub Issues**: https://github.com/bearsampp/module-memcached/issues +- **Bearsampp Issues**: https://github.com/bearsampp/bearsampp/issues +- **Documentation**: https://bearsampp.com/module/memcached + +--- + +**Last Updated**: 2025-08-20 +**Version**: 2025.8.20 +**Build System**: Pure Gradle (no Ant) + +Notes: +- This project deliberately does not ship the Gradle Wrapper. Install Gradle 8+ locally and run with `gradle ...`. +- Legacy Ant files (e.g., Eclipse `.launch` referencing `build.xml`) are deprecated and not used by the build. diff --git a/.gradle-docs/RELEASE-PROCESS.md b/.gradle-docs/RELEASE-PROCESS.md deleted file mode 100644 index ab5456a..0000000 --- a/.gradle-docs/RELEASE-PROCESS.md +++ /dev/null @@ -1,799 +0,0 @@ -# Release Process Guide - -Complete guide for creating and publishing releases of Bearsampp Module Memcached. - -## Table of Contents - -- [Overview](#overview) -- [Prerequisites](#prerequisites) -- [Release Workflow](#release-workflow) -- [Step-by-Step Guide](#step-by-step-guide) -- [Version Management](#version-management) -- [Publishing Releases](#publishing-releases) -- [Post-Release Tasks](#post-release-tasks) -- [Troubleshooting](#troubleshooting) - -## Overview - -The release process involves: - -1. **Preparation** - Verify environment and version -2. **Building** - Create release package -3. **Testing** - Verify package integrity -4. **Publishing** - Upload to GitHub releases -5. **Documentation** - Update release notes and properties - -### Release Timeline - -``` -┌─────────────┐ -│ Preparation │ (15 min) -└──────┬──────┘ - │ - ▼ -┌─��───────────┐ -│ Building │ (5 min) -└──────┬──────┘ - │ - ▼ -┌─────────────┐ -│ Testing │ (10 min) -└──────┬──────┘ - │ - ▼ -┌─────────────┐ -│ Publishing │ (10 min) -└──────┬──────┘ - │ - ▼ -┌─────────────┐ -│ Done │ -└─────────────┘ - -Total: ~40 minutes -``` - ---- - -## Prerequisites - -### Required Tools - -| Tool | Version | Purpose | Installation | -|------------------|--------------|--------------------------------------------|-----------------------------------------| -| Java | 8+ | Run Gradle build | [Download](https://adoptium.net/) | -| Gradle | 7.0+ | Build automation | [Download](https://gradle.org/) | -| 7-Zip | Latest | Create .7z archives | [Download](https://www.7-zip.org/) | -| Git | Latest | Version control | [Download](https://git-scm.com/) | - -### Required Access - -| Access Type | Purpose | Required For | -|---------------------|--------------------------------------------|-----------------------------------------| -| GitHub Write | Push commits and tags | Publishing releases | -| GitHub Releases | Create and upload releases | Publishing releases | - -### Environment Setup - -```bash -# Verify Java -java -version - -# Verify Gradle -gradle --version - -# Verify 7-Zip -7z - -# Verify Git -git --version - -# Verify build environment -gradle verify -``` - -**Expected Output:** - -``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Build Environment Verification ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -Environment Check Results: -──────────────────────────────────���──────────────────────────────────��────── - ✓ PASS Java 8+ - ✓ PASS build.gradle - ✓ PASS build.properties - ✓ PASS releases.properties - ✓ PASS settings.gradle - ✓ PASS bin/ directory - ✓ PASS 7z command -──────────────────────────────────────────────────────────────────────────── - -[SUCCESS] All checks passed! Build environment is ready. -``` - ---- - -## Release Workflow - -### Standard Release Flow - -``` -1. Check for new Memcached version - ↓ -2. Download and prepare binaries - ↓ -3. Add to bin/ directory - ↓ -4. Update build.properties - ↓ -5. Build release package - ↓ -6. Test package - ↓ -7. Create Git tag - ↓ -8. Push to GitHub - ↓ -9. Create GitHub release - ↓ -10. Upload package - ↓ -11. Update releases.properties - ↓ -12. Commit and push -``` - ---- - -## Step-by-Step Guide - -### Step 1: Check for New Version - -Check Memcached official releases: - -**Source:** https://memcached.org/downloads - -**Example:** -``` -Latest version: 1.6.40 -Current version: 1.6.39 -Action: Proceed with release -``` - ---- - -### Step 2: Download Binaries - -Download Windows binaries for the new version. - -**Sources:** -- Official Memcached builds -- Third-party Windows builds -- Compiled from source - -**Verify:** -- `memcached.exe` is present -- Version matches expected -- All dependencies included - ---- - -### Step 3: Prepare Binary Directory - -Create version directory in `bin/`: - -```bash -# Create directory -mkdir bin/memcached1.6.40 - -# Copy binaries -copy memcached.exe bin/memcached1.6.40/ -copy *.dll bin/memcached1.6.40/ - -# Verify -dir bin/memcached1.6.40 -``` - -**Expected Structure:** -``` -bin/memcached1.6.40/ -├── memcached.exe -├── pthreadGC2.dll -└── README.txt (optional) -``` - ---- - -### Step 4: Update Configuration - -Update `build.properties` with new release date: - -```properties -bundle.name = memcached -bundle.release = 2025.9.15 -bundle.type = bins -bundle.format = 7z -``` - -**Release Date Format:** `YYYY.M.D` or `YYYY.MM.DD` - -**Examples:** -- `2025.9.15` - September 15, 2025 -- `2025.12.1` - December 1, 2025 - ---- - -### Step 5: Verify Configuration - -```bash -# Validate properties -gradle validateProperties - -# List available versions -gradle listVersions - -# Verify environment -gradle verify -``` - -**Expected Output:** - -``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Validating build.properties ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -[SUCCESS] All required properties are present: - -Property Value -────────────────────────────────────────────────────── -bundle.name memcached -bundle.release 2025.9.15 -bundle.type bins -bundle.format 7z -────────────────────────────────────────────────────── -``` - ---- - -### Step 6: Build Release Package - -Build the release package: - -```bash -gradle release -PbundleVersion=1.6.40 -``` - -**Expected Output:** - -``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Release Build ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -Building release for memcached version 1.6.40... - -Bundle path: E:/Bearsampp-development/module-memcached/bin/memcached1.6.40 - -Preparing bundle... -Copying bundle files... -Creating archive: bearsampp-memcached-1.6.40-2025.9.15.7z - -╔════════════════════════════════════════════════════════════════════════════╗ -║ Release Build Completed ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -Release package created: - C:\Users\troy\Bearsampp-build\release\bearsampp-memcached-1.6.40-2025.9.15.7z - -Package size: 0.45 MB -``` - -**Output Location:** -``` -${buildPath}/release/bearsampp-memcached-1.6.40-2025.9.15.7z -``` - ---- - -### Step 7: Test Package - -Verify the release package: - -```bash -# Extract to test directory -7z x bearsampp-memcached-1.6.40-2025.9.15.7z -otest/ - -# Verify contents -dir test/memcached1.6.40 - -# Test executable -test/memcached1.6.40/memcached.exe -h -``` - -**Verification Checklist:** - -- [ ] Archive extracts without errors -- [ ] All files present -- [ ] `memcached.exe` runs -- [ ] Version is correct -- [ ] No missing dependencies - ---- - -### Step 8: Create Git Tag - -Create and push Git tag: - -```bash -# Stage changes -git add bin/memcached1.6.40/ -git add build.properties - -# Commit -git commit -m "Add memcached 1.6.40" - -# Create tag -git tag -a 2025.9.15 -m "Release 2025.9.15 - Memcached 1.6.40" - -# Push commit -git push origin main - -# Push tag -git push origin 2025.9.15 -``` - -**Tag Format:** `YYYY.M.D` or `YYYY.MM.DD` - -**Tag Message Format:** -``` -Release YYYY.M.D - Memcached X.X.X - -- Add memcached X.X.X -- Update build configuration -- Update documentation -``` - ---- - -### Step 9: Create GitHub Release - -Create release on GitHub: - -**URL:** https://github.com/bearsampp/module-memcached/releases/new - -**Release Form:** - -| Field | Value | -|---------------------|----------------------------------------------------------------| -| Tag | `2025.9.15` | -| Release Title | `Memcached 1.6.40 - 2025.9.15` | -| Description | See template below | -| Attach Files | `bearsampp-memcached-1.6.40-2025.9.15.7z` | - -**Release Description Template:** - -```markdown -# Memcached 1.6.40 - Release 2025.9.15 - -## What's New - -- Updated to Memcached 1.6.40 -- Latest Windows binaries -- Compatible with Bearsampp 2025.x - -## Download - -- **File:** bearsampp-memcached-1.6.40-2025.9.15.7z -- **Size:** 0.45 MB -- **Format:** 7z - -## Installation - -1. Download the archive -2. Extract to Bearsampp modules directory -3. Restart Bearsampp - -## Changelog - -### Memcached 1.6.40 - -- Bug fixes and improvements -- Performance enhancements -- Security updates - -## Requirements - -- Bearsampp 2025.x or later -- Windows 10/11 (64-bit) - -## Links - -- [Memcached Official](https://memcached.org/) -- [Bearsampp Project](https://github.com/bearsampp/bearsampp) -- [Documentation](https://bearsampp.com/module/memcached) - -## Support - -Report issues at: https://github.com/bearsampp/bearsampp/issues -``` - ---- - -### Step 10: Upload Package - -Upload the release package to GitHub: - -1. Go to the release page -2. Click "Edit release" -3. Drag and drop `bearsampp-memcached-1.6.40-2025.9.15.7z` -4. Wait for upload to complete -5. Click "Update release" - -**Verify Upload:** -- File appears in release assets -- Download link works -- File size is correct - ---- - -### Step 11: Update releases.properties - -Add new release entry: - -```properties -# releases.properties - -# ... existing entries ... - -1.6.40 = https://github.com/Bearsampp/module-memcached/releases/download/2025.9.15/bearsampp-memcached-1.6.40-2025.9.15.7z -``` - -**URL Format:** -``` -https://github.com/Bearsampp/module-memcached/releases/download/{tag}/bearsampp-memcached-{version}-{release}.7z -``` - -**Sort Order:** Ascending by version number - ---- - -### Step 12: Commit and Push - -Commit the updated releases.properties: - -```bash -# Stage changes -git add releases.properties - -# Commit -git commit -m "Update releases.properties for 1.6.40" - -# Push -git push origin main -``` - ---- - -### Step 13: Verify Release - -Verify the release is complete: - -```bash -# List releases -gradle listReleases - -# Check latest release -curl -s https://api.github.com/repos/bearsampp/module-memcached/releases/latest | grep tag_name -``` - -**Verification Checklist:** - -- [ ] Release appears on GitHub -- [ ] Package is downloadable -- [ ] releases.properties is updated -- [ ] Git tag exists -- [ ] Documentation is updated - ---- - -## Version Management - -### Version Numbering - -Memcached uses semantic versioning: - -``` -MAJOR.MINOR.PATCH - │ │ │ - │ │ └─── Bug fixes - │ └───────── New features (backward compatible) - └─────────────── Breaking changes -``` - -**Examples:** -- `1.6.39` - Version 1, minor 6, patch 39 -- `1.6.40` - Version 1, minor 6, patch 40 -- `2.0.0` - Version 2, minor 0, patch 0 - -### Release Numbering - -Bearsampp releases use date-based versioning: - -``` -YYYY.M.D - │ │ │ - │ │ └─── Day - │ └───── Month - └───────── Year -``` - -**Examples:** -- `2025.9.15` - September 15, 2025 -- `2025.12.1` - December 1, 2025 -- `2026.1.20` - January 20, 2026 - -### Version Matrix - -| Memcached Version | Release Date | Bearsampp Release | Status | -|------------------|--------------|-------------------|-------------| -| 1.6.39 | 2025.8.20 | 2025.8.20 | Current | -| 1.6.38 | 2025.4.19 | 2025.4.19 | Supported | -| 1.6.36 | 2025.2.11 | 2025.2.11 | Supported | -| 1.6.33 | 2024.12.23 | 2024.12.23 | Supported | -| 1.6.32 | 2024.12.1 | 2024.12.1 | Supported | - ---- - -## Publishing Releases - -### GitHub Release Checklist - -- [ ] Tag created and pushed -- [ ] Release created on GitHub -- [ ] Release title follows format -- [ ] Release description is complete -- [ ] Package uploaded -- [ ] Package is downloadable -- [ ] releases.properties updated -- [ ] Documentation updated - -### Release Assets - -Each release should include: - -| Asset | Type | Required | Description | -|-----------------------------------------------|----------|----------|--------------------------------| -| `bearsampp-memcached-{version}-{date}.7z` | Binary | Yes | Release package | -| Release notes | Text | Yes | Changelog and information | - -### Release Metadata - -GitHub release metadata: - -```json -{ - "tag_name": "2025.9.15", - "name": "Memcached 1.6.40 - 2025.9.15", - "draft": false, - "prerelease": false, - "body": "Release description...", - "assets": [ - { - "name": "bearsampp-memcached-1.6.40-2025.9.15.7z", - "content_type": "application/x-7z-compressed" - } - ] -} -``` - ---- - -## Post-Release Tasks - -### Update Documentation - -Update project documentation: - -1. **README.md** - Update version badges -2. **.gradle-docs/** - Update documentation -3. **CHANGELOG.md** - Add release notes (if exists) - -### Announce Release - -Announce the release: - -1. **GitHub Discussions** - Post announcement -2. **Project Website** - Update downloads page -3. **Social Media** - Share release (if applicable) - -### Monitor Issues - -Monitor for issues: - -1. Check GitHub issues -2. Monitor download statistics -3. Review user feedback - ---- - -## Troubleshooting - -### Common Issues - -#### Issue: Build fails - -**Error:** -``` -Bundle version not found: E:/Bearsampp-development/module-memcached/bin/memcached1.6.40 -``` - -**Solution:** -1. Verify directory exists: `dir bin/memcached1.6.40` -2. Check directory name matches version -3. Ensure binaries are present - ---- - -#### Issue: 7z compression fails - -**Error:** -``` -7z compression failed with exit code: 2 -``` - -**Solution:** -1. Verify 7z is installed: `7z` -2. Check PATH includes 7-Zip -3. Verify disk space available -4. Check file permissions - ---- - -#### Issue: Git push fails - -**Error:** -``` -! [rejected] main -> main (fetch first) -``` - -**Solution:** -```bash -# Pull latest changes -git pull origin main - -# Resolve conflicts if any -git mergetool - -# Push again -git push origin main -``` - ---- - -#### Issue: GitHub release upload fails - -**Error:** -``` -Failed to upload asset -``` - -**Solution:** -1. Check file size (< 2GB) -2. Verify internet connection -3. Try uploading via web interface -4. Check GitHub status - ---- - -#### Issue: Package extraction fails - -**Error:** -``` -Cannot open file as archive -``` - -**Solution:** -1. Verify package integrity -2. Re-download package -3. Rebuild package -4. Check 7z version - ---- - -### Debug Commands - -```bash -# Verify build -gradle verify --info - -# Test build -gradle release -PbundleVersion=1.6.40 --debug - -# Check Git status -git status -git log --oneline -5 - -# Verify package -7z t bearsampp-memcached-1.6.40-2025.9.15.7z - -# Test extraction -7z x bearsampp-memcached-1.6.40-2025.9.15.7z -otest/ -``` - ---- - -## Release Checklist - -### Pre-Release - -- [ ] New version available -- [ ] Binaries downloaded -- [ ] Directory created in `bin/` -- [ ] `build.properties` updated -- [ ] Environment verified -- [ ] Configuration validated - -### Build - -- [ ] Release package built -- [ ] Package tested -- [ ] Package size verified -- [ ] Contents verified - -### Publish - -- [ ] Git changes committed -- [ ] Git tag created -- [ ] Changes pushed to GitHub -- [ ] GitHub release created -- [ ] Package uploaded -- [ ] Release published - -### Post-Release - -- [ ] `releases.properties` updated -- [ ] Changes committed and pushed -- [ ] Release verified -- [ ] Documentation updated -- [ ] Announcement posted - ---- - -## Best Practices - -### Version Control - -- Always create tags for releases -- Use descriptive commit messages -- Keep release history in `releases.properties` -- Document breaking changes - -### Testing - -- Test package before publishing -- Verify all files are included -- Check executable runs correctly -- Test on clean system if possible - -### Documentation - -- Keep documentation up-to-date -- Document known issues -- Provide clear installation instructions -- Include changelog - -### Communication - -- Announce releases clearly -- Respond to user feedback -- Monitor for issues -- Provide support - ---- - -**Last Updated:** 2025-08-20 -**Version:** 2025.8.20 -**Maintainer:** Bearsampp Team diff --git a/.gradle-docs/TASKS.md b/.gradle-docs/TASKS.md index cf02d78..424caa2 100644 --- a/.gradle-docs/TASKS.md +++ b/.gradle-docs/TASKS.md @@ -1,91 +1,125 @@ # Gradle Tasks Reference -Complete reference for all available Gradle tasks in the Bearsampp Module Memcached build system. +Complete reference for all Gradle tasks in the Bearsampp Module Memcached build system. + +--- ## Table of Contents +- [Task Groups](#task-groups) - [Build Tasks](#build-tasks) - [Verification Tasks](#verification-tasks) -- [Help Tasks](#help-tasks) -- [Documentation Tasks](#documentation-tasks) +- [Information Tasks](#information-tasks) - [Task Dependencies](#task-dependencies) -- [Custom Properties](#custom-properties) +- [Task Examples](#task-examples) + +--- + +## Task Groups + +Tasks are organized into logical groups: + +| Group | Purpose | +|------------------|--------------------------------------------------| +| **build** | Build and package tasks | +| **verification** | Verification and validation tasks | +| **help** | Help and information tasks | + +--- ## Build Tasks ### release -Build a release package for a specific Memcached version. - -**Group:** `build` +Build and package a release for a specific Memcached version. -**Syntax:** +**Group**: build +**Usage**: ```bash -# Interactive mode (lists available versions) +# Interactive mode (prompts for version) gradle release -# Non-interactive mode (builds specific version) -gradle release -PbundleVersion= +# Non-interactive mode (specify version) +gradle release -PbundleVersion=1.6.29 ``` -**Parameters:** +**Parameters**: +- `-PbundleVersion=X.X.X` - Memcached version to build (optional, prompts if not provided) -| Parameter | Type | Required | Description | Example | -|-------------------|----------|----------|---------------------------------------|--------------| -| `bundleVersion` | String | No | Version to build (e.g., "1.6.39") | `1.6.39` | +**Description**: +- Validates the specified version exists in `bin/` or `bin/archived/` +- Copies Memcached files to preparation directory +- Creates archive in configured format (7z or zip) +- Generates hash files (MD5, SHA1, SHA256, SHA512) +- Outputs to `bearsampp-build/bins/memcached/{bundle.release}/` -**Examples:** +**Output**: +``` +bearsampp-build/bins/memcached/2025.8.20/ +├── bearsampp-memcached-1.6.29-2025.8.20.7z +├── bearsampp-memcached-1.6.29-2025.8.20.7z.md5 +├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha1 +├── bearsampp-memcached-1.6.29-2025.8.20.7z.sha256 +└── bearsampp-memcached-1.6.29-2025.8.20.7z.sha512 +``` +**Example**: ```bash -# Interactive mode - shows available versions -gradle release - -# Build version 1.6.39 -gradle release -PbundleVersion=1.6.39 +# Build version 1.6.29 +gradle release -PbundleVersion=1.6.29 -# Build version 1.6.38 -gradle release -PbundleVersion=1.6.38 +# Interactive mode - prompts for version selection +gradle release ``` -**Output:** +--- -``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Release Build ║ -╚════════════════════════════════════════════════════════════════════════════╝ +### releaseAll -Building release for memcached version 1.6.39... +Build releases for all available Memcached versions in the `bin/` directory. -Bundle path: E:/Bearsampp-development/module-memcached/bin/memcached1.6.39 +**Group**: build -Preparing bundle... -Copying bundle files... -Creating archive: bearsampp-memcached-1.6.39-2025.8.20.7z +**Usage**: +```bash +gradle releaseAll +``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Release Build Completed ║ -╚════════════════════════════════════════════════════════════════════════════╝ +**Description**: +- Scans `bin/` and `bin/archived/` directories for all Memcached versions +- Builds each version sequentially +- Reports success/failure for each version +- Provides summary at the end -Release package created: - C:\Users\troy\Bearsampp-build\release\bearsampp-memcached-1.6.39-2025.8.20.7z +**Example**: +```bash +gradle releaseAll +``` -Package size: 0.45 MB +**Output**: ``` +====================================================================== +Building releases for 3 memcached versions +====================================================================== -**Process:** +[1/3] Building memcached 1.6.29... +[SUCCESS] memcached 1.6.29 completed -1. Validates the specified version exists in `bin/` directory -2. Creates temporary build directory -3. Copies bundle files to temporary location -4. Creates 7z archive with proper naming convention -5. Outputs archive location and size +[2/3] Building memcached 1.6.39... +[SUCCESS] memcached 1.6.39 completed -**Error Handling:** +[3/3] Building memcached 1.5.22... +[SUCCESS] memcached 1.5.22 completed -- **Version not found:** Lists available versions -- **Missing 7z:** Provides installation instructions -- **Permission errors:** Suggests running with elevated privileges +====================================================================== +Build Summary +====================================================================== +Total versions: 3 +Successful: 3 +Failed: 0 +====================================================================== +``` --- @@ -93,40 +127,22 @@ Package size: 0.45 MB Clean build artifacts and temporary files. -**Group:** `build` - -**Syntax:** +**Group**: build +**Usage**: ```bash gradle clean ``` -**Examples:** +**Description**: +- Removes Gradle build directory +- Cleans temporary build artifacts +**Example**: ```bash -# Clean all build artifacts gradle clean - -# Clean and rebuild -gradle clean release -PbundleVersion=1.6.39 ``` -**Output:** - -``` -Cleaned: E:\Bearsampp-development\module-memcached\build -Cleaned: C:\Users\troy\Bearsampp-build\tmp - -[SUCCESS] Build artifacts cleaned -``` - -**Cleaned Directories:** - -| Directory | Description | -|-------------------------------------------|--------------------------------| -| `build/` | Gradle build directory | -| `${buildPath}/tmp/` | Temporary build files | - --- ## Verification Tasks @@ -135,231 +151,231 @@ Cleaned: C:\Users\troy\Bearsampp-build\tmp Verify the build environment and dependencies. -**Group:** `verification` - -**Syntax:** +**Group**: verification +**Usage**: ```bash gradle verify ``` -**Examples:** +**Description**: +Checks the following: +- Java version (8+) +- Required files (build.properties) +- Dev directory (optional warning) +- bin/ directory +- 7-Zip installation (if format is 7z) +**Example**: ```bash -# Verify build environment gradle verify - -# Verify with detailed output -gradle verify --info ``` -**Output:** - +**Output**: ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Build Environment Verification ║ -╚════════════════════════════════════════════════════════════════════════════╝ +Verifying build environment for module-memcached... Environment Check Results: -──────────────────────────────────────────────────────────────────────────── - ✓ PASS Java 8+ - ✓ PASS build.gradle - ✓ PASS build.properties - ✓ PASS releases.properties - ✓ PASS settings.gradle - �� PASS bin/ directory - ✓ PASS 7z command -──────────────────────────────────────────────────────────────────────────── +------------------------------------------------------------ + [PASS] Java 8+ + [PASS] build.properties + [FAIL] dev directory + [PASS] bin directory + [PASS] 7-Zip +------------------------------------------------------------ [SUCCESS] All checks passed! Build environment is ready. You can now run: - gradle release -PbundleVersion=1.6.39 - Build specific version - gradle listVersions - List available versions + gradle release -PbundleVersion=1.6.29 - Build release for version + gradle listVersions - List available versions ``` -**Checks Performed:** - -| Check | Description | Requirement | -|-----------------------|------------------------------------------------|------------------| -| Java 8+ | Java version 8 or higher | Required | -| build.gradle | Main build script exists | Required | -| build.properties | Bundle configuration exists | Required | -| releases.properties | Release history exists | Required | -| settings.gradle | Gradle settings exists | Required | -| bin/ directory | Binary directory exists | Required | -| 7z command | 7-Zip command available | Required | - --- ### validateProperties -Validate build.properties configuration. - -**Group:** `verification` +Validate the build.properties configuration file. -**Syntax:** +**Group**: verification +**Usage**: ```bash gradle validateProperties ``` -**Examples:** +**Description**: +Validates that all required properties are present: +- `bundle.name` +- `bundle.release` +- `bundle.type` +- `bundle.format` +**Example**: ```bash -# Validate properties gradle validateProperties ``` -**Output:** +**Output**: +``` +Validating build.properties... +[SUCCESS] All required properties are present: + bundle.name = memcached + bundle.release = 2025.8.20 + bundle.type = bins + bundle.format = 7z +``` + +--- + +### checkModulesUntouched +Check integration with the modules-untouched repository. + +**Group**: verification + +**Usage**: +```bash +gradle checkModulesUntouched ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Validating build.properties ║ -╚════════════════════════════════════════════════════════════════════════════╝ -[SUCCESS] All required properties are present: +**Description**: +- Fetches memcached.properties from modules-untouched repository +- Displays available versions +- Tests repository connectivity + +**Example**: +```bash +gradle checkModulesUntouched +``` -Property Value -────────────────────────────────────────────────── -bundle.name memcached -bundle.release 2025.8.20 -bundle.type bins -bundle.format 7z -────────────────────────────────────────────────── +**Output**: ``` +====================================================================== +Modules-Untouched Integration Check +====================================================================== -**Validated Properties:** +Repository URL: + https://raw.githubusercontent.com/Bearsampp/modules-untouched/main/modules/memcached.properties -| Property | Required | Description | -|------------------|----------|---------------------------------------| -| `bundle.name` | Yes | Bundle name (e.g., "memcached") | -| `bundle.release` | Yes | Release date (e.g., "2025.8.20") | -| `bundle.type` | Yes | Bundle type (e.g., "bins") | -| `bundle.format` | Yes | Archive format (e.g., "7z") | +Fetching memcached.properties from modules-untouched... + +====================================================================== +Available Versions in modules-untouched +====================================================================== + 1.6.29 + 1.6.39 + 1.5.22 +====================================================================== +Total versions: 3 + +====================================================================== +[SUCCESS] modules-untouched integration is working +====================================================================== +``` --- -## Help Tasks +## Information Tasks ### info Display build configuration information. -**Group:** `help` - -**Syntax:** +**Group**: help +**Usage**: ```bash gradle info ``` -**Examples:** +**Description**: +Displays comprehensive build information including: +- Project details +- Bundle properties +- File paths +- Java and Gradle versions +- Available task groups +- Quick start commands +**Example**: ```bash -# Display build information gradle info ``` -**Output:** - +**Output**: ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Bearsampp Module Memcached - Build Info ║ -╚════════════════════════════════════════════════════════════════════════════╝ +================================================================ + Bearsampp Module Memcached - Build Info +================================================================ -Project Information: - Name: module-memcached - Version: 2025.8.20 - Description: Bearsampp Module - memcached - Group: com.bearsampp.modules +Project: module-memcached +Version: 2025.8.20 +Description: Bearsampp Module - memcached Bundle Properties: - Name: memcached - Release: 2025.8.20 - Type: bins - Format: 7z + Name: memcached + Release: 2025.8.20 + Type: bins + Format: 7z Paths: - Project Directory: E:/Bearsampp-development/module-memcached - Root Directory: E:/Bearsampp-development - Build Path: C:\Users\troy\Bearsampp-build - Temp Path: C:\Users\troy\Bearsampp-build\tmp - Release Path: C:\Users\troy\Bearsampp-build\release - -Environment: - Java Version: 17.0.2 - Java Home: C:\Program Files\Java\jdk-17.0.2 - Gradle Version: 8.5 - Gradle Home: C:\Users\troy\.gradle\wrapper\dists\gradle-8.5 - Operating System: Windows 11 10.0 - -Available Task Groups: - • build - Build and package tasks - • verification - Verification and validation tasks - • help - Help and information tasks - • documentation - Documentation tasks + Project Dir: E:/Bearsampp-development/module-memcached + Root Dir: E:/Bearsampp-development + Build Base: E:/Bearsampp-development/bearsampp-build + Build Tmp: E:/Bearsampp-development/bearsampp-build/tmp + ... + +Java: + Version: 17 + Home: C:/Program Files/Java/jdk-17 + +Gradle: + Version: 8.5 + Home: C:/Gradle/gradle-8.5 Quick Start: gradle tasks - List all available tasks gradle info - Show this information - gradle release - Interactive release build - gradle release -PbundleVersion=1.6.39 - Non-interactive release + gradle release - Build release (interactive mode) + gradle release -PbundleVersion=1.6.29 - Build release for version + gradle releaseAll - Build all available versions gradle clean - Clean build artifacts - gradle verify - Verify build environment - gradle listVersions - List available bundle versions - gradle listReleases - List all releases - -Documentation: - See .gradle-docs/ for comprehensive build documentation ``` --- ### listVersions -List all available bundle versions in the bin/ directory. - -**Group:** `help` +List all available Memcached versions in the `bin/` and `bin/archived/` directories. -**Syntax:** +**Group**: help +**Usage**: ```bash gradle listVersions ``` -**Examples:** +**Description**: +- Scans `bin/` directory for Memcached versions +- Scans `bin/archived/` directory for archived versions +- Displays versions with location tags +- Shows total count +**Example**: ```bash -# List available versions gradle listVersions ``` -**Output:** - +**Output**: ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Available memcached Versions in bin/ ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -Version Size Path -──────────────────────────────────────────────────────────────────────────── -1.6.15 0.42 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.15 -1.6.17 0.43 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.17 -1.6.18 0.43 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.18 -1.6.21 0.44 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.21 -1.6.24 0.44 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.24 -1.6.29 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.29 -1.6.31 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.31 -1.6.32 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.32 -1.6.33 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.33 -1.6.36 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.36 -1.6.37 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.37 -1.6.38 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.38 -1.6.39 0.45 MB E:/Bearsampp-development/module-memcached/bin/memcached1.6.39 -─────────────────────────────────────────────────────���────────────────────── - -Total versions: 13 +Available memcached versions: +------------------------------------------------------------ + 1.6.29 [bin] + 1.6.39 [bin] + 1.5.22 [bin/archived] +------------------------------------------------------------ +Total versions: 3 To build a specific version: gradle release -PbundleVersion=1.6.39 @@ -369,226 +385,236 @@ To build a specific version: ### listReleases -List all available releases from releases.properties. +List all available Memcached releases from the modules-untouched repository. -**Group:** `help` - -**Syntax:** +**Group**: help +**Usage**: ```bash gradle listReleases ``` -**Examples:** +**Description**: +- Fetches memcached.properties from modules-untouched +- Displays all available versions with download URLs +- Shows total count +**Example**: ```bash -# List all releases gradle listReleases ``` -**Output:** - +**Output**: ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ Available Memcached Releases ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -Version Download URL -──────────────────────────────────────────────────────────────────────────── -1.6.6 https://github.com/Bearsampp/module-memcached/releases/download/2022.07.14/bearsampp-memcached-1.6.6-2022.07.14.7z -1.6.15 https://github.com/Bearsampp/module-memcached/releases/download/2022.08.04/bearsampp-memcached-1.6.15-2022.08.04.7z -1.6.17 https://github.com/Bearsampp/module-memcached/releases/download/2022.09.26/bearsampp-memcached-1.6.17-2022.09.24.7z -1.6.18 https://github.com/Bearsampp/module-memcached/releases/download/2023.3.5/bearsampp-memcached-1.6.18-2023.3.5.7z -1.6.21 https://github.com/Bearsampp/module-memcached/releases/download/2023.10.1/bearsampp-memcached-1.6.21-2023.10.1.7z -1.6.24 https://github.com/Bearsampp/module-memcached/releases/download/2024.3.30/bearsampp-memcached-1.6.24-2024.3.30.7z -1.6.29 https://github.com/Bearsampp/module-memcached/releases/download/2024.7.29/bearsampp-memcached-1.6.29-2024.10.7.7z -1.6.31 https://github.com/Bearsampp/module-memcached/releases/download/2024.10.7/bearsampp-memcached-1.6.31-2024.10.7.7z -1.6.32 https://github.com/Bearsampp/module-memcached/releases/download/2024.12.1/bearsampp-memcached-1.6.32-2024.12.1.7z -1.6.33 https://github.com/Bearsampp/module-memcached/releases/download/2024.12.23/bearsampp-memcached-1.6.33-2024.12.23.7z -1.6.36 https://github.com/Bearsampp/module-memcached/releases/download/2025.2.11/bearsampp-memcached-1.6.36-2025.2.11.7z -1.6.38 https://github.com/Bearsampp/module-memcached/releases/download/2025.4.19/bearsampp-memcached-1.6.38-2025.4.19.7z -1.6.39 https://github.com/Bearsampp/module-memcached/releases/download/2025.8.20/bearsampp-memcached-1.6.39-2025.8.20.7z -──────────────────────────────────────────────────────────────────────────── - -Total releases: 13 +Available Memcached Releases (modules-untouched): +-------------------------------------------------------------------------------- + 1.6.29 -> https://memcached.org/files/memcached-1.6.29-win64.zip + 1.6.39 -> https://memcached.org/files/memcached-1.6.39-win64.zip + 1.5.22 -> https://memcached.org/files/memcached-1.5.22-win64.zip +-------------------------------------------------------------------------------- +Total releases: 3 ``` --- -### tasks - -List all available Gradle tasks. +## Task Dependencies -**Group:** `help` +### Task Execution Order -**Syntax:** +Tasks have implicit dependencies based on their functionality: -```bash -gradle tasks -gradle tasks --all ``` +release + └── (validates environment) + └── (validates version exists) + └── (copies files) + └── (creates archive) + └── (generates hashes) -**Examples:** +releaseAll + └── (scans for versions) + └── (calls release logic for each version) -```bash -# List main tasks -gradle tasks +verify + └── (checks Java) + └── (checks files) + └── (checks directories) + └── (checks 7-Zip) + +validateProperties + └── (loads build.properties) + └── (validates required properties) -# List all tasks including internal ones -gradle tasks --all +checkModulesUntouched + └── (fetches remote properties) + └── (displays versions) ``` --- -## Documentation Tasks +## Task Examples -### generateDocs +### Building a Single Version -Generate build documentation in .gradle-docs/. +```bash +# Interactive mode - prompts for version +gradle release -**Group:** `documentation` +# Non-interactive mode - specify version +gradle release -PbundleVersion=1.6.29 +``` -**Syntax:** +### Building All Versions ```bash -gradle generateDocs +# Build all versions in bin/ and bin/archived/ +gradle releaseAll ``` -**Examples:** +### Verifying Environment ```bash -# Generate documentation -gradle generateDocs -``` +# Check if environment is ready for building +gradle verify -**Output:** +# Validate build.properties +gradle validateProperties -``` -Generating documentation in .gradle-docs/... -[SUCCESS] Documentation directory created -See .gradle-docs/ for comprehensive build documentation +# Check modules-untouched integration +gradle checkModulesUntouched ``` ---- +### Getting Information -## Task Dependencies +```bash +# Display build information +gradle info -### Dependency Graph +# List local versions +gradle listVersions -``` -release - (no dependencies) +# List remote versions +gradle listReleases -clean - (no dependencies) +# List all available tasks +gradle tasks +``` -verify - (no dependencies) +### Cleaning Build Artifacts -validateProperties - (no dependencies) +```bash +# Clean build directory +gradle clean +``` -info - (no dependencies) +### Debugging -listVersions - (no dependencies) +```bash +# Run with info logging +gradle release -PbundleVersion=1.6.29 --info -listReleases - (no dependencies) +# Run with debug logging +gradle release -PbundleVersion=1.6.29 --debug -generateDocs - (no dependencies) +# Run with stack trace on error +gradle release -PbundleVersion=1.6.29 --stacktrace ``` -### Common Task Chains +### Combining Tasks ```bash # Clean and build -gradle clean release -PbundleVersion=1.6.39 +gradle clean release -PbundleVersion=1.6.29 # Verify and build -gradle verify release -PbundleVersion=1.6.39 +gradle verify release -PbundleVersion=1.6.29 -# Validate and build -gradle validateProperties release -PbundleVersion=1.6.39 - -# Full verification and build -gradle clean verify validateProperties release -PbundleVersion=1.6.39 +# List versions and build +gradle listVersions release ``` --- -## Custom Properties - -### Project Properties - -Properties that can be passed via command line: - -| Property | Type | Description | Example | -|------------------|----------|---------------------------------------|----------------------------------------------| -| `bundleVersion` | String | Version to build | `-PbundleVersion=1.6.39` | +## Task Options -### System Properties +### Global Gradle Options -System properties that can be set: +| Option | Description | +|---------------------|----------------------------------------------| +| `--info` | Set log level to INFO | +| `--debug` | Set log level to DEBUG | +| `--stacktrace` | Print stack trace on error | +| `--scan` | Create build scan | +| `--offline` | Execute build without network access | +| `--refresh-dependencies` | Refresh cached dependencies | -| Property | Type | Description | Example | -|--------------------------|-----------|----------------------------------|------------------------------------------| -| `org.gradle.daemon` | Boolean | Enable Gradle daemon | `-Dorg.gradle.daemon=true` | -| `org.gradle.parallel` | Boolean | Enable parallel execution | `-Dorg.gradle.parallel=true` | -| `org.gradle.caching` | Boolean | Enable build caching | `-Dorg.gradle.caching=true` | - -### Environment Variables - -Environment variables used by the build: +### Project Properties -| Variable | Description | Example | -|------------------|---------------------------------------|----------------------------------------------| -| `JAVA_HOME` | Java installation directory | `C:\Program Files\Java\jdk-17.0.2` | -| `GRADLE_HOME` | Gradle installation directory | `C:\Gradle\gradle-8.5` | -| `PATH` | System path (must include 7z) | `C:\Program Files\7-Zip;...` | +| Property | Description | Example | +|---------------------|----------------------------------------------|----------------------------------| +| `-PbundleVersion` | Specify Memcached version to build | `-PbundleVersion=1.6.29` | --- -## Task Output Formats +## Task Output ### Success Output ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ [Task Name] Completed ║ -╚════════════════════════════════════════════════════════════════════════════╝ - -[SUCCESS] Task completed successfully +====================================================================== +[SUCCESS] Release build completed successfully for version 1.6.29 +Output directory: E:/Bearsampp-development/bearsampp-build/tmp/bundles_build/bins/memcached/memcached1.6.29 +Archive: E:/Bearsampp-development/bearsampp-build/bins/memcached/2025.8.20/bearsampp-memcached-1.6.29-2025.8.20.7z +====================================================================== ``` ### Error Output ``` -╔════════════════════════════════════════════════════════════════════════════╗ -║ [Task Name] Failed ║ -╚════════════════════════════════════════════════════════════════════════════╝ +FAILURE: Build failed with an exception. -[ERROR] Error message here +* What went wrong: +Execution failed for task ':release'. +> Bundle version not found in bin/ or bin/archived/ -Possible solutions: - • Solution 1 - • Solution 2 +Available versions: + - 1.6.29 + - 1.6.39 + - 1.5.22 ``` -### Warning Output +--- -``` -[WARNING] Warning message here +## Best Practices -Please review: - • Item 1 - • Item 2 -``` +### Task Usage + +1. **Always verify first**: Run `gradle verify` before building +2. **List versions**: Use `gradle listVersions` to see available versions +3. **Use non-interactive mode in scripts**: Specify `-PbundleVersion` for automation +4. **Clean when needed**: Run `gradle clean` if you encounter issues +5. **Check logs**: Use `--info` or `--debug` for troubleshooting + +### Performance Tips + +1. **Enable Gradle daemon**: Set `org.gradle.daemon=true` in gradle.properties +2. **Use parallel execution**: Set `org.gradle.parallel=true` +3. **Enable caching**: Set `org.gradle.caching=true` +4. **Increase heap size**: Set `org.gradle.jvmargs=-Xmx2g` + +--- + +## Support + +For task-related issues: + +- **Documentation**: [README.md](README.md) +- **Configuration**: [CONFIGURATION.md](CONFIGURATION.md) +- **GitHub Issues**: https://github.com/bearsampp/module-memcached/issues +- **Bearsampp Issues**: https://github.com/bearsampp/bearsampp/issues --- -**Last Updated:** 2025-08-20 -**Version:** 2025.8.20 -**Maintainer:** Bearsampp Team +**Last Updated**: 2025-08-20 +**Version**: 2025.8.20 +**Build System**: Pure Gradle diff --git a/DOCS-UPDATE-SUMMARY.md b/DOCS-UPDATE-SUMMARY.md new file mode 100644 index 0000000..8d9385d --- /dev/null +++ b/DOCS-UPDATE-SUMMARY.md @@ -0,0 +1,141 @@ +# Documentation Update Summary + +## Overview + +Updated the module-memcached documentation structure to match the reference implementation from module-php (gradle-convert branch). + +## Changes Made + +### Files Updated + +1. **README.md** (root) + - Simplified and modernized structure + - Added table format for prerequisites and tasks + - Removed verbose explanations in favor of links to detailed docs + - Matches module-php reference style + +2. **.gradle-docs/INDEX.md** + - Complete rewrite to match reference structure + - Improved navigation and organization + - Added comprehensive keyword search + - Better document summaries + - Reduced from verbose to concise format + +3. **.gradle-docs/README.md** + - Complete rewrite with better structure + - Added table of contents + - Improved architecture section with clear diagrams + - Better troubleshooting section + - Enhanced examples and verification commands + - Matches module-php documentation style + +4. **.gradle-docs/TASKS.md** (created) + - Complete task reference documentation + - Detailed descriptions for all tasks + - Usage examples for each task + - Task dependencies and execution order + - Best practices and tips + +5. **.gradle-docs/CONFIGURATION.md** (created) + - Comprehensive configuration guide + - Detailed property descriptions + - Environment variable documentation + - Build path configuration + - Archive configuration details + - Configuration examples + - Troubleshooting section + +6. **.gradle-docs/MIGRATION.md** (created) + - Complete Ant to Gradle migration guide + - Command mapping table + - File changes documentation + - Configuration changes + - Task equivalents + - Benefits comparison + - Migration checklist + +### Files Removed + +The following redundant/obsolete files were removed: + +1. **.gradle-docs/QUICK_REFERENCE.md** - Content merged into README.md and INDEX.md +2. **.gradle-docs/INTERACTIVE_MODE.md** - Content merged into TASKS.md +3. **.gradle-docs/FEATURE_SUMMARY.md** - Content merged into README.md +4. **.gradle-docs/MODULES_UNTOUCHED_INTEGRATION.md** - Content merged into README.md and TASKS.md +5. **.gradle-docs/CONFIGURATION_SUMMARY.md** - Replaced by comprehensive CONFIGURATION.md +6. **.gradle-docs/MIGRATION_SUMMARY.md** - Replaced by comprehensive MIGRATION.md +7. **.gradle-docs/RELEASE-PROCESS.md** - Not applicable to memcached (simpler build process) +8. **.gradle-docs/MIGRATION-GUIDE.md** - Replaced by MIGRATION.md + +## Final Documentation Structure + +``` +.gradle-docs/ +├── INDEX.md # Documentation index and navigation +├── README.md # Main documentation and quick start +├── TASKS.md # Complete task reference +├── CONFIGURATION.md # Configuration guide +└── MIGRATION.md # Ant to Gradle migration guide +``` + +## Key Improvements + +### Structure +- Reduced from 12 files to 5 focused documents +- Eliminated redundancy and overlap +- Clear separation of concerns +- Better navigation and cross-referencing + +### Content +- More concise and focused +- Better examples and code blocks +- Improved tables and formatting +- Consistent style across all documents +- Better troubleshooting sections + +### Usability +- Easier to find information +- Clear task descriptions +- Comprehensive configuration guide +- Step-by-step migration guide +- Better cross-references between documents + +## Reference + +Based on: +- https://github.com/Bearsampp/module-php/tree/gradle-convert/.gradle-docs +- https://github.com/Bearsampp/module-php/tree/gradle-convert/README.md + +## Verification + +To verify the documentation: + +1. Check structure: + ```bash + dir .gradle-docs + ``` + +2. Review main docs: + ```bash + type README.md + type .gradle-docs\README.md + type .gradle-docs\INDEX.md + ``` + +3. Test documentation links: + - Open INDEX.md and verify all links work + - Check cross-references between documents + +## Next Steps + +1. Review the updated documentation +2. Test all examples and commands +3. Verify all links work correctly +4. Update any external references to old documentation +5. Commit changes to repository + +--- + +**Date**: 2025-11-17 +**Updated By**: Documentation Update Script +**Reference**: module-php gradle-convert branch diff --git a/README.md b/README.md index ae1cb6e..371ea7d 100644 --- a/README.md +++ b/README.md @@ -5,24 +5,9 @@ This is a module of [Bearsampp project](https://github.com/bearsampp/bearsampp) involving Memcached. -## Documentation and downloads +## Build System -https://bearsampp.com/module/memcached - -## Building - -This module uses Gradle for building releases. The build system provides modern features including: -- Native Gradle build (no Ant dependency) -- Automatic hash file generation (MD5, SHA1, SHA256, SHA512) -- Integration with modules-untouched repository -- Support for both bin/ and bin/archived/ directories -- Batch building of all versions - -### Prerequisites - -- Java 8 or higher -- Gradle (wrapper included) -- 7-Zip (for .7z format archives) +This project uses **Gradle** as its build system. The legacy Ant build has been fully replaced with a modern, pure Gradle implementation. ### Quick Start @@ -30,63 +15,55 @@ This module uses Gradle for building releases. The build system provides modern # Display build information gradle info +# List all available tasks +gradle tasks + # Verify build environment gradle verify -# List available versions in bin/ directory -gradle listVersions +# Build a release (interactive) +gradle release -# Build release for specific version (non-interactive) +# Build a specific version (non-interactive) gradle release -PbundleVersion=1.6.29 -# Build release interactively (prompts for version) -gradle release - -# Build all available versions +# Build all versions gradle releaseAll # Clean build artifacts gradle clean ``` -### Available Tasks - -**Build Tasks:** -- `gradle release -PbundleVersion=X.X.X` - Build release package for specific version -- `gradle releaseAll` - Build releases for all available versions -- `gradle clean` - Clean build artifacts - -**Help Tasks:** -- `gradle info` - Display build configuration information -- `gradle tasks` - List all available tasks -- `gradle listVersions` - List available versions in bin/ and bin/archived/ -- `gradle listReleases` - List available releases from modules-untouched - -**Verification Tasks:** -- `gradle verify` - Verify build environment and dependencies -- `gradle validateProperties` - Validate build.properties configuration -- `gradle checkModulesUntouched` - Check modules-untouched integration - -### Build Configuration - -Build settings are configured in `build.properties`: - -```properties -bundle.name = memcached -bundle.release = 2025.8.20 -bundle.type = bins -bundle.format = 7z -``` +### Prerequisites -### Output +| Requirement | Version | Purpose | +|-------------------|---------------|------------------------------------------| +| **Java** | 8+ | Required for Gradle execution | +| **Gradle** | 8.0+ | Build automation tool | +| **7-Zip** | Latest | Archive creation (.7z format) | -Built releases are placed in: -- Build directory: `C:/Bearsampp-build/memcachedX.X.X/` -- Archive format: `.7z` (configurable) -- Hash files: `.md5`, `.sha1`, `.sha256`, `.sha512` +### Available Tasks -Archive structure: -- Each archive contains a top-level version folder, matching Bearsampp’s historical packaging (e.g., `memcached1.6.39/` with all files inside). +| Task | Description | +|-----------------------|--------------------------------------------------| +| `release` | Build release package (interactive/non-interactive) | +| `releaseAll` | Build releases for all available versions | +| `clean` | Clean build artifacts and temporary files | +| `verify` | Verify build environment and dependencies | +| `info` | Display build configuration information | +| `listVersions` | List available bundle versions in bin/ | +| `listReleases` | List available releases from modules-untouched | +| `validateProperties` | Validate build.properties configuration | +| `checkModulesUntouched` | Check modules-untouched integration | + +For complete documentation, see [.gradle-docs/README.md](.gradle-docs/README.md) + +## Documentation + +- **Build Documentation**: [.gradle-docs/README.md](.gradle-docs/README.md) +- **Task Reference**: [.gradle-docs/TASKS.md](.gradle-docs/TASKS.md) +- **Configuration Guide**: [.gradle-docs/CONFIGURATION.md](.gradle-docs/CONFIGURATION.md) +- **Module Downloads**: https://bearsampp.com/module/memcached ## Issues From 5f2502f7b06bef9991abaa76ba5bacefb78ce42f Mon Sep 17 00:00:00 2001 From: Bear Date: Tue, 18 Nov 2025 20:56:02 -0600 Subject: [PATCH 6/7] fixes memcached build --- .gradle-docs/README.md | 55 ++++- README.md | 21 +- build-bundle-ant.xml | 203 ++++++++++++++++ build-commons-ant.xml | 510 +++++++++++++++++++++++++++++++++++++++++ build.gradle | 147 ++++++++++-- build.xml.reference | 38 +++ 6 files changed, 949 insertions(+), 25 deletions(-) create mode 100644 build-bundle-ant.xml create mode 100644 build-commons-ant.xml create mode 100644 build.xml.reference diff --git a/.gradle-docs/README.md b/.gradle-docs/README.md index 863c866..363861d 100644 --- a/.gradle-docs/README.md +++ b/.gradle-docs/README.md @@ -296,13 +296,62 @@ bearsampp-build/bins/memcached/2025.8.20/ ### Modules-Untouched Integration -The build system integrates with the [modules-untouched repository](https://github.com/Bearsampp/modules-untouched) to fetch version information: +The build system **automatically downloads and extracts** Memcached versions from the [modules-untouched repository](https://github.com/Bearsampp/modules-untouched), similar to how the legacy Ant build worked: - **Repository**: `https://github.com/Bearsampp/modules-untouched` - **Properties File**: `modules/memcached.properties` -- **Purpose**: Centralized version management and download URLs +- **Purpose**: Centralized version management and automatic downloads -The `listReleases` and `checkModulesUntouched` tasks query this repository to display available Memcached versions. +#### How It Works + +When you run `gradle release -PbundleVersion=1.6.29`, the build: + +1. **Fetches** the `memcached.properties` file from modules-untouched +2. **Looks up** the download URL for version 1.6.29 +3. **Downloads** the archive (cached in `bearsampp-build/tmp/bundles_src/`) +4. **Extracts** the archive automatically +5. **Validates** that memcached.exe exists +6. **Builds** the release package + +#### Download Caching + +Downloaded archives are cached in `bearsampp-build/tmp/bundles_src/memcached/`: +- First build: Downloads from modules-untouched +- Subsequent builds: Reuses cached download +- Saves bandwidth and speeds up builds + +#### Supported Archive Formats + +The build automatically handles: +- `.7z` - 7-Zip archives +- `.zip` - ZIP archives +- `.tar.gz` - Gzipped tar archives + +#### No Local bin/ Directory Required + +Unlike the old workflow, you **don't need** to manually download and place files in `bin/`: +- The build downloads directly from modules-untouched +- Archives are extracted automatically +- Nested directory structures (like `memcached-x86/`) are handled + +#### Verification Tasks + +| Task | Description | +|---------------------------|----------------------------------------------| +| `listReleases` | List all versions available in modules-untouched | +| `checkModulesUntouched` | Verify connection and list available versions | + +Example: +```bash +# See what versions are available +gradle listReleases + +# Verify modules-untouched integration +gradle checkModulesUntouched + +# Build any available version +gradle release -PbundleVersion=1.6.38 +``` --- diff --git a/README.md b/README.md index 371ea7d..f2123eb 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,23 @@ This is a module of [Bearsampp project](https://github.com/bearsampp/bearsampp) This project uses **Gradle** as its build system. The legacy Ant build has been fully replaced with a modern, pure Gradle implementation. +### Key Features + +- **Automatic Downloads**: Fetches Memcached versions from [modules-untouched](https://github.com/Bearsampp/modules-untouched) +- **Smart Caching**: Downloads are cached to speed up subsequent builds +- **Multiple Formats**: Supports .7z, .zip, and .tar.gz archives +- **Hash Generation**: Automatically creates MD5, SHA1, SHA256, and SHA512 checksums + ### Quick Start ```bash # Display build information gradle info -# List all available tasks -gradle tasks - -# Verify build environment -gradle verify +# List available versions from modules-untouched +gradle listReleases -# Build a release (interactive) -gradle release - -# Build a specific version (non-interactive) +# Build a release (downloads automatically from modules-untouched) gradle release -PbundleVersion=1.6.29 # Build all versions @@ -34,6 +35,8 @@ gradle releaseAll gradle clean ``` +**Note**: The build automatically downloads and extracts Memcached versions from the modules-untouched repository. You don't need to manually download or place files in the `bin/` directory. + ### Prerequisites | Requirement | Version | Purpose | diff --git a/build-bundle-ant.xml b/build-bundle-ant.xml new file mode 100644 index 0000000..cebd883 --- /dev/null +++ b/build-bundle-ant.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build-commons-ant.xml b/build-commons-ant.xml new file mode 100644 index 0000000..c489f9f --- /dev/null +++ b/build-commons-ant.xml @@ -0,0 +1,510 @@ + + + + + + + + + + + + + + + + + + + + + + num2 ? 1 : (num1 < num2 ? -1 : 0))); + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${hash_list.item.md5} ${hash_list.item.filename} + ${hash_list.item.sha1} ${hash_list.item.filename} + ${hash_list.item.sha-256} ${hash_list.item.filename} + ${hash_list.item.sha-512} ${hash_list.item.filename} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build.gradle b/build.gradle index 0c1e9cf..7300500 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,135 @@ def fetchModulesUntouchedProperties() { } } +// Helper function to get module from modules-untouched (like Ant's getmoduleuntouched macro) +def getModuleUntouched(String version) { + println "** Get Module Untouched" + println "* Name : ${bundleName}" + println "* Version : ${version}" + + // Fetch the properties file + def untouchedProps = fetchModulesUntouchedProperties() + if (!untouchedProps) { + throw new GradleException("Could not fetch modules-untouched properties for version ${version}") + } + + // Get the URL for this version + def downloadUrl = untouchedProps.getProperty(version) + if (!downloadUrl) { + throw new GradleException("Version ${version} not found in modules-untouched properties") + } + + println "* Url : ${downloadUrl}" + + // Extract filename from URL + def filename = downloadUrl.tokenize('/').last() + println "* Filename : ${filename}" + + // Setup paths + def moduleTmpPath = file("${bundleTmpSrcPath}/${bundleName}") + def destFolder = "${bundleName}${version}" + def destPath = file("${moduleTmpPath}/${destFolder}") + + println "* Destination : ${destPath}" + + // Download the file if not already present + def downloadedFile = file("${moduleTmpPath}/${filename}") + if (!downloadedFile.exists()) { + println "Downloading ${filename}..." + moduleTmpPath.mkdirs() + + def connection = new URL(downloadUrl).openConnection() + connection.setRequestProperty("User-Agent", "Bearsampp-Build") + downloadedFile.withOutputStream { out -> + connection.inputStream.withCloseable { inp -> + out << inp + } + } + println "Downloaded: ${downloadedFile}" + } else { + println "Using cached: ${downloadedFile}" + } + + // Extract the archive + if (destPath.exists()) { + delete destPath + } + destPath.mkdirs() + + println "Processing ${filename}..." + + if (filename.endsWith('.7z') || filename.endsWith('.7z.exe')) { + // Extract 7z archive + def sevenZipExe = find7ZipExecutable() + if (!sevenZipExe) { + throw new GradleException("7-Zip executable not found! Please install 7-Zip.") + } + + def command = [ + sevenZipExe, + 'x', + downloadedFile.absolutePath, + "-o${destPath.absolutePath}", + '-y' + ] + + def process = new ProcessBuilder(command as String[]) + .redirectErrorStream(true) + .start() + + process.inputStream.eachLine { line -> + println line + } + + def exitCode = process.waitFor() + if (exitCode != 0) { + throw new GradleException("7-Zip extraction failed with exit code: ${exitCode}") + } + + } else if (filename.endsWith('.zip')) { + // Extract ZIP archive + copy { + from zipTree(downloadedFile) + into destPath + } + } else if (filename.endsWith('.tar.gz')) { + // Extract tar.gz archive + copy { + from tarTree(resources.gzip(downloadedFile)) + into destPath + } + } else { + throw new GradleException("Unknown archive format: ${filename}") + } + + println "Extracted to: ${destPath}" + + // Check for nested directory structure (some archives have memcached-x86 subdirectory) + def memcachedX86 = file("${destPath}/memcached-x86") + if (memcachedX86.exists() && memcachedX86.isDirectory()) { + println "Found memcached-x86 subdirectory, using that as source" + return memcachedX86 + } + + // Verify memcached.exe exists + def memcachedExe = file("${destPath}/memcached.exe") + if (!memcachedExe.exists()) { + // Check if there's a single subdirectory with memcached.exe + def subdirs = destPath.listFiles()?.findAll { it.isDirectory() } + if (subdirs && subdirs.size() == 1) { + def subdir = subdirs[0] + memcachedExe = file("${subdir}/memcached.exe") + if (memcachedExe.exists()) { + println "Found memcached.exe in subdirectory: ${subdir.name}" + return subdir + } + } + throw new GradleException("memcached.exe not found in extracted archive at ${destPath}") + } + + return destPath +} + // Helper function to find 7-Zip executable def find7ZipExecutable() { // Check environment variable @@ -372,19 +501,11 @@ tasks.register('release') { println "=".multiply(70) println "" - // Validate version exists - check both bin and bin/archived directories - def bundlePath = file("${projectDir}/bin/${bundleName}${versionToBuild}") - if (!bundlePath.exists()) { - bundlePath = file("${projectDir}/bin/archived/${bundleName}${versionToBuild}") - if (!bundlePath.exists()) { - throw new GradleException("Bundle version not found in bin/ or bin/archived/\n\nAvailable versions:\n${getAvailableVersions().collect { " - ${it}" }.join('\n')}") - } - } + // Get module from modules-untouched (downloads and extracts if needed) + def bundleSrcFinal = getModuleUntouched(versionToBuild) - def bundleSrcDest = bundlePath - def bundleSrcFinal = bundleSrcDest - - println "Bundle path: ${bundleSrcFinal}" + println "" + println "Source ready: ${bundleSrcFinal}" println "" // Get the bundle folder and version @@ -401,7 +522,7 @@ tasks.register('release') { // Copy Memcached files println "Copying Memcached files..." copy { - from bundleSrcDest + from bundleSrcFinal into memcachedPrepPath } diff --git a/build.xml.reference b/build.xml.reference new file mode 100644 index 0000000..82b1b0d --- /dev/null +++ b/build.xml.reference @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fb8a8a253529ee9e237bf735cafa72e50ca24cb4 Mon Sep 17 00:00:00 2001 From: Bear Date: Tue, 18 Nov 2025 21:32:24 -0600 Subject: [PATCH 7/7] fixes memcached build --- bin/memcached1.6.39/memcached.exe | Bin 265728 -> 0 bytes build.gradle | 26 ++++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) delete mode 100644 bin/memcached1.6.39/memcached.exe diff --git a/bin/memcached1.6.39/memcached.exe b/bin/memcached1.6.39/memcached.exe deleted file mode 100644 index 82dec7b1b1ac94f00bc193727bc38b381a93c6b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265728 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

Et}QC+Fj&RMUQ) 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 G-~RzXL>y`W diff --git a/build.gradle b/build.gradle index 7300500..714d7f9 100644 --- a/build.gradle +++ b/build.gradle @@ -519,13 +519,35 @@ tasks.register('release') { } memcachedPrepPath.mkdirs() - // Copy Memcached files - println "Copying Memcached files..." + // Copy Memcached files from modules-untouched + println "Copying Memcached files from modules-untouched..." copy { from bundleSrcFinal into memcachedPrepPath } + // Overlay Bearsampp-specific files from bin directory + def binOverlayPath = file("${projectDir}/bin/${bundleName}${bundleVersion}") + def archivedOverlayPath = file("${projectDir}/bin/archived/${bundleName}${bundleVersion}") + + def overlaySource = null + if (binOverlayPath.exists()) { + overlaySource = binOverlayPath + } else if (archivedOverlayPath.exists()) { + overlaySource = archivedOverlayPath + } + + if (overlaySource) { + println "Overlaying Bearsampp-specific files from ${overlaySource}..." + copy { + from overlaySource + into memcachedPrepPath + exclude '**/.gitkeep' + } + } else { + println "Warning: No overlay files found in bin/${bundleName}${bundleVersion} or bin/archived/${bundleName}${bundleVersion}" + } + println "" println "Copying to bundles_build directory..." def memcachedBuildPath = file("${bundleTmpBuildPath}/${bundleName}${bundleVersion}")