diff --git a/.gitignore b/.gitignore index 7cb04d1e..b5856ece 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ out *.iml *.iws .idea + +.claude/settings.local.json diff --git a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationDaemonPlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationDaemonPlugin.groovy index b805114b..4a05083d 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationDaemonPlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationDaemonPlugin.groovy @@ -24,7 +24,7 @@ import groovy.transform.CompileDynamic import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.tasks.application.CreateStartScripts +import org.gradle.api.plugins.JavaApplication /** * Combine the nebula-ospackage-application with the nebula-ospackage-daemon plugin. As with the nebula-ospackage-application, @@ -46,24 +46,23 @@ class OspackageApplicationDaemonPlugin implements Plugin { project.plugins.apply(OspackageApplicationPlugin) def ospackageApplicationExtension = project.extensions.getByType(OspackageApplicationExtension) - CreateStartScripts startScripts = (CreateStartScripts) project.tasks.getByName(ApplicationPlugin.TASK_START_SCRIPTS_NAME) - project.plugins.apply(OspackageDaemonPlugin) // Mechanism for user to configure daemon further List daemonConfiguration = [] setApplicationDaemon(project, daemonConfiguration) - // TODO Convention mapping on definition instead of afterEvaluate + // Keep afterEvaluate to wait for user configuration of applicationName project.afterEvaluate { - // TODO Sanitize name - def name = startScripts.applicationName + // Use strongly-typed application extension instead of eager task realization + JavaApplication appExtension = project.extensions.getByType(JavaApplication) + def name = appExtension.applicationName ?: project.name // Add daemon to project DaemonExtension daemonExt = project.extensions.getByType(DaemonExtension) def definition = daemonExt.daemon { DaemonDefinition daemonDefinition -> daemonDefinition.setDaemonName(name) - daemonDefinition.setCommand("${ospackageApplicationExtension.prefix}/${name}/bin/${name}".toString()) + daemonDefinition.setCommand("${ospackageApplicationExtension.prefix.get()}/${name}/bin/${name}".toString()) } daemonConfiguration.each { confClosure -> diff --git a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationExtension.groovy b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationExtension.groovy index 3d007336..5f41f308 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationExtension.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationExtension.groovy @@ -16,8 +16,9 @@ package com.netflix.gradle.plugins.application -class OspackageApplicationExtension { - String prefix +import org.gradle.api.provider.Property - String distribution +interface OspackageApplicationExtension { + Property getPrefix() + Property getDistribution() } diff --git a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationPlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationPlugin.groovy index abfaa9b3..6d510605 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationPlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationPlugin.groovy @@ -18,9 +18,9 @@ package com.netflix.gradle.plugins.application import com.netflix.gradle.plugins.packaging.ProjectPackagingExtension import com.netflix.gradle.plugins.packaging.SystemPackagingPlugin +import groovy.transform.CompileDynamic import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.IConventionAware import org.gradle.api.distribution.DistributionContainer import org.gradle.api.distribution.plugins.DistributionPlugin import org.gradle.api.plugins.ApplicationPlugin @@ -39,20 +39,21 @@ class OspackageApplicationPlugin implements Plugin { OspackageApplicationExtension extension @Override + @CompileDynamic void apply(Project project) { extension = project.extensions.create('ospackage_application', OspackageApplicationExtension) - def conventionMapping = ((IConventionAware) extension).conventionMapping - conventionMapping.map('prefix') { '/opt' } - conventionMapping.map('distribution') { '' } + extension.prefix.convention('/opt') + extension.distribution.convention('') project.plugins.apply(ApplicationPlugin) project.plugins.apply(SystemPackagingPlugin) def distributions = project.getExtensions().getByType(DistributionContainer.class) def mainDistribution = distributions.getByName(DistributionPlugin.MAIN_DISTRIBUTION_NAME) - def name = mainDistribution.getDistributionBaseName().map { baseName -> - def classifier = mainDistribution.getDistributionClassifier().getOrNull() - baseName + (classifier != null ? '-' + classifier : '') + def name = extension.prefix.map { prefix -> + String baseName = mainDistribution.getDistributionBaseName().get() + String classifier = mainDistribution.getDistributionClassifier().getOrNull() + return baseName + (classifier != null ? '-' + classifier : '') } def packaging = project.extensions.getByType(ProjectPackagingExtension) def copyMain = project.copySpec() { @@ -60,6 +61,6 @@ class OspackageApplicationPlugin implements Plugin { into(name) } packaging.with(copyMain) - packaging.into(project.provider { extension.prefix }) + packaging.into(extension.prefix.map { it }) } } diff --git a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationSpringBootPlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationSpringBootPlugin.groovy index 1ffd48e2..08938445 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationSpringBootPlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/application/OspackageApplicationSpringBootPlugin.groovy @@ -56,8 +56,16 @@ class OspackageApplicationSpringBootPlugin implements Plugin { void apply(Project project) { project.plugins.apply(OspackageApplicationPlugin) + // Validate Spring Boot plugin at configuration time using plugins.withId + // This automatically waits for plugin application + boolean springBootFound = false + project.plugins.withId("org.springframework.boot") { + springBootFound = true + } + + // Only validate after other plugins have a chance to apply project.afterEvaluate { - if (!project.plugins.hasPlugin('org.springframework.boot')) { + if (!springBootFound) { project.logger.error("The '{}' plugin requires the '{}' plugin.", "com.netflix.nebula.ospackage-application-spring-boot", "org.springframework.boot") @@ -116,9 +124,10 @@ class OspackageApplicationSpringBootPlugin implements Plugin { // Workaround for https://github.com/gradle/gradle/issues/16371 if (GradleVersion.current().baseVersion >= GradleVersion.version('6.4').baseVersion) { + def mainClass = project.application.mainClass project.tasks.named(ApplicationPlugin.TASK_START_SCRIPTS_NAME).configure { doFirst { - if (!project.application.mainClass.isPresent()) { + if (!mainClass.isPresent()) { throw new GradleException("mainClass should be configured in order to generate a valid start script. i.e. mainClass = 'com.netflix.app.MyApp'") } } @@ -151,8 +160,10 @@ class OspackageApplicationSpringBootPlugin implements Plugin { main { contents { into('lib') { - project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).files.findAll { file -> - file.getName() != project.tasks.getByName(JavaPlugin.JAR_TASK_NAME).outputs.files.singleFile.name + def jarTaskProvider = project.tasks.named(JavaPlugin.JAR_TASK_NAME) + def runtimeClasspath = project.configurations.named(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + runtimeClasspath.get().files.findAll { file -> + file.getName() != jarTaskProvider.get().outputs.files.singleFile.name }.each { file -> exclude file.name } diff --git a/src/main/groovy/com/netflix/gradle/plugins/daemon/DaemonTemplateTask.groovy b/src/main/groovy/com/netflix/gradle/plugins/daemon/DaemonTemplateTask.groovy index 4cbe4851..f4057cde 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/daemon/DaemonTemplateTask.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/daemon/DaemonTemplateTask.groovy @@ -16,12 +16,14 @@ package com.netflix.gradle.plugins.daemon -import org.gradle.api.internal.ConventionTask +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFiles +import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.work.DisableCachingByDefault @@ -29,36 +31,37 @@ import org.gradle.work.DisableCachingByDefault * Monster class that does everything. */ @DisableCachingByDefault -class DaemonTemplateTask extends ConventionTask { - - DaemonTemplateTask() { - notCompatibleWithConfigurationCache("nebula.ospackage does not support configuration cache") - } +abstract class DaemonTemplateTask extends DefaultTask { @Internal - Map context + abstract MapProperty getContext() @Internal - Collection templates + abstract ListProperty getTemplates() + + @OutputDirectory + abstract DirectoryProperty getDestDir() @Internal - File destDir + abstract Property getTemplatesFolder() @Internal - String templatesFolder + abstract Property getProjectDirectory() - @TaskAction - def template() { - TemplateHelper templateHelper = new TemplateHelper(getDestDir(), getTemplatesFolder(), project) - getTemplates().collect { String templateName -> - templateHelper.generateFile(templateName, getContext()) - } + DaemonTemplateTask() { + // Capture project directory during configuration + projectDirectory.convention(project.projectDir) } - @Internal - Collection getTemplatesOutput() { - return templates.collect { - new File(destDir, it) + @TaskAction + def template() { + TemplateHelper templateHelper = new TemplateHelper( + destDir.get().asFile, + templatesFolder.get(), + projectDirectory.get() + ) + templates.get().collect { String templateName -> + templateHelper.generateFile(templateName, context.get()) } } } diff --git a/src/main/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPlugin.groovy index 6860dc67..0a4fe7e0 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPlugin.groovy @@ -29,7 +29,6 @@ import org.gradle.api.Project class OspackageDaemonPlugin implements Plugin { public static final String POST_INSTALL_TEMPLATE = "postInstall" - Project project DaemonExtension extension DaemonTemplatesConfigExtension daemonTemplatesConfigExtension DefaultDaemonDefinitionExtension defaultDefinition @@ -54,7 +53,6 @@ class OspackageDaemonPlugin implements Plugin { @Override void apply(Project project) { - this.project = project project.plugins.apply(SystemPackagingBasePlugin) DomainObjectSet daemonsList = WrapUtil.toDomainObjectSet(DaemonDefinition) @@ -92,7 +90,7 @@ class OspackageDaemonPlugin implements Plugin { String cleanedName = daemonName.replaceAll("\\W", "").capitalize() - File outputDir = new File(project.layout.buildDirectory.getAsFile().get(), "daemon/${cleanedName}/${task.name}") + def outputDirProvider = project.layout.buildDirectory.dir("daemon/${cleanedName}/${task.name}") String defaultInitDScriptLocationTemplate = isRedhat ? "/etc/rc.d/init.d/\${daemonName}" : "/etc/init.d/\${daemonName}" Map templatesWithFileOutput = [ @@ -101,33 +99,54 @@ class OspackageDaemonPlugin implements Plugin { 'initd': defaultDefinition.initDScriptLocation ?: defaultInitDScriptLocationTemplate ] - DaemonTemplateTask templateTask = project.tasks.create("${task.name}${cleanedName}Daemon".toString(), DaemonTemplateTask) - templateTask.conventionMapping.map('destDir') { outputDir } - templateTask.conventionMapping.map('templatesFolder') { daemonTemplatesConfigExtension.folder ?: DEFAULT_TEMPLATES_FOLDER } - templateTask.conventionMapping.map('context') { - Map context = toContext(defaults, definition) - context.daemonName = daemonName - context.isRedhat = isRedhat - context.installCmd = definition.installCmd ?: LegacyInstallCmd.create(context) - context + def templateTaskProvider = project.tasks.register("${task.name}${cleanedName}Daemon", DaemonTemplateTask) { + // Use Property API instead of conventionMapping + it.destDir.convention(outputDirProvider) + it.templatesFolder.convention(daemonTemplatesConfigExtension.folder ?: DEFAULT_TEMPLATES_FOLDER) + it.context.convention(project.providers.provider { + Map context = toContext(defaults, definition) + context.daemonName = daemonName + context.isRedhat = isRedhat + context.installCmd = definition.installCmd ?: LegacyInstallCmd.create(context) + context + }) + it.templates.convention(templatesWithFileOutput.keySet() + POST_INSTALL_TEMPLATE) } - templateTask.conventionMapping.map('templates') { templatesWithFileOutput.keySet() + POST_INSTALL_TEMPLATE } - task.dependsOn(templateTask) + task.dependsOn(templateTaskProvider) templatesWithFileOutput.each { String templateName, String destPathTemplate -> - File rendered = new File(outputDir, templateName) // To be created by task, ok that it's not around yet - String destPath = getDestPath(destPathTemplate, templateTask) - // Gradle CopySpec can't set the name of a file on the fly, we need to do a rename. - int slashIdx = destPath.lastIndexOf('/') - String destDir = destPath.substring(0,slashIdx) - String destFile = destPath.substring(slashIdx+1) - configureTask(task, rendered, destDir, destFile) + // Use lazy providers to avoid eager task realization + def renderedFileProvider = outputDirProvider.map { dir -> + new File(dir.asFile, templateName) + } + + def destPathProvider = templateTaskProvider.flatMap { templateTask -> + project.providers.provider { + getDestPath(destPathTemplate, templateTask) + } + } + + // Configure task with lazy providers + configureTaskLazily(task, renderedFileProvider, destPathProvider) } - task.doFirst { - File postInstallCommand = new File(outputDir, POST_INSTALL_TEMPLATE) - task.postInstall(postInstallCommand.text) - } + // Add postInstall content from generated template + // Use providers.fileContents() which is configuration-cache-safe + def postInstallFileProvider = project.layout.buildDirectory.file( + "daemon/${cleanedName}/${task.name}/${POST_INSTALL_TEMPLATE}" + ) + + // Use fileContents provider which properly handles file reading for config cache + def postInstallContentProvider = project.providers.fileContents(postInstallFileProvider) + .asText + .orElse('') + + // Add the file content to postInstallCommands + task.exten.postInstallCommands.addAll( + postInstallContentProvider.map { String content -> + content?.trim() ? [content] : [] + } + ) } } } @@ -142,6 +161,25 @@ class OspackageDaemonPlugin implements Plugin { } } + @CompileDynamic + private void configureTaskLazily(SystemPackagingTask task, def renderedFileProvider, def destPathProvider) { + task.from(renderedFileProvider) { + // Use closures for lazy evaluation during copy execution + into({ + String destPath = destPathProvider.get() + int slashIdx = destPath.lastIndexOf('/') + destPath.substring(0, slashIdx) + }) + rename({ String filename -> + String destPath = destPathProvider.get() + int slashIdx = destPath.lastIndexOf('/') + destPath.substring(slashIdx + 1) + }) + FilePermissionUtil.setFilePermission(it, 0555) + user 'root' + } + } + @CompileDynamic private void addDaemonToProject(Project project, Closure closure) { project.ext.daemon = closure @@ -149,7 +187,7 @@ class OspackageDaemonPlugin implements Plugin { private String getDestPath(String destPathTemplate, DaemonTemplateTask templateTask) { GStringTemplateEngine engine = new GStringTemplateEngine() - def destPath = engine.createTemplate(destPathTemplate).make(templateTask.getContext()).toString() + def destPath = engine.createTemplate(destPathTemplate).make(templateTask.getContext().get()).toString() destPath } diff --git a/src/main/groovy/com/netflix/gradle/plugins/daemon/TemplateHelper.groovy b/src/main/groovy/com/netflix/gradle/plugins/daemon/TemplateHelper.groovy index c1784a4b..f9fd864e 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/daemon/TemplateHelper.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/daemon/TemplateHelper.groovy @@ -17,7 +17,6 @@ package com.netflix.gradle.plugins.daemon import groovy.text.GStringTemplateEngine -import org.gradle.api.Project import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -28,10 +27,10 @@ class TemplateHelper { File destDir String templatesFolder - Project project + File projectDir - TemplateHelper(File destDir, String templatesFolder, Project project) { - this.project = project + TemplateHelper(File destDir, String templatesFolder, File projectDir) { + this.projectDir = projectDir this.destDir = destDir this.templatesFolder = templatesFolder } @@ -53,8 +52,15 @@ class TemplateHelper { private InputStream getTemplateContent(String templateName) { try { - String path = "${templatesFolder}/${templateName}.tpl" - return getClass().getResourceAsStream(path) ?: project.file(path).newInputStream() + String path = templatesFolder ? "${templatesFolder}/${templateName}.tpl" : "${templateName}.tpl" + InputStream stream = getClass().getResourceAsStream(path) + if (stream) { + return stream + } + // If path is absolute (and templatesFolder is not empty), use it directly; otherwise resolve from projectDir + File templateFile = (templatesFolder && new File(templatesFolder).isAbsolute()) ? + new File(path) : new File(projectDir, path) + return templateFile.newInputStream() } catch(Exception e) { throw new FileNotFoundException("Could not find template $templateName in $templatesFolder") } diff --git a/src/main/groovy/com/netflix/gradle/plugins/deb/Deb.groovy b/src/main/groovy/com/netflix/gradle/plugins/deb/Deb.groovy index 07195ee6..3eaddb48 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/deb/Deb.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/deb/Deb.groovy @@ -21,9 +21,9 @@ import com.netflix.gradle.plugins.packaging.Dependency import com.netflix.gradle.plugins.packaging.SystemPackagingTask import com.netflix.gradle.plugins.utils.DeprecationLoggerUtils import groovy.transform.CompileDynamic +import org.gradle.api.Project import org.gradle.api.file.ProjectLayout -import org.gradle.api.internal.ConventionMapping -import org.gradle.api.internal.IConventionAware +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile @@ -39,7 +39,6 @@ abstract class Deb extends SystemPackagingTask { Deb(ProjectLayout projectLayout) { super(projectLayout) archiveExtension.set 'deb' - notCompatibleWithConfigurationCache("nebula.ospackage does not support configuration cache") } @Override @@ -49,7 +48,8 @@ abstract class Deb extends SystemPackagingTask { name += getVersion() ? "_${getVersion()}" : '' name += getRelease() ? "-${getRelease()}" : '' name += getArchString() ? "_${getArchString()}" : '' - name += getArchiveExtension().getOrNull() ? ".${getArchiveExtension().getOrNull()}" : '' + def ext = getArchiveExtension().getOrNull() + name += ext ? ".${ext}" : '' } return name; @@ -61,58 +61,84 @@ abstract class Deb extends SystemPackagingTask { } @Override - protected void applyConventions() { - super.applyConventions() - - // For all mappings, we're only being called if it wasn't explicitly set on the task. In which case, we'll want - // to pull from the parentExten. And only then would we fallback on some other value. - ConventionMapping mapping = ((IConventionAware) this).getConventionMapping() - - // Could come from extension - mapping.map('fileType', { parentExten?.getFileType() }) - mapping.map('uid', { parentExten?.getUid()?:0 }) - mapping.map('gid', { (parentExten?.getGid())?:0 }) - mapping.map('packageGroup', { parentExten?.getPackageGroup() ?: 'java' }) - mapping.map('multiArch', { parentExten?.getMultiArch() }) - mapping.map('archStr', { parentExten?.getArchStr()?:'all'}) - mapping.map('maintainer', { parentExten?.getMaintainer() ?: System.getProperty('user.name', '') }) - mapping.map('uploaders', { parentExten?.getUploaders() ?: '' }) - mapping.map('priority', { parentExten?.getPriority() ?: 'optional' }) + protected void applyConventions(Project project) { + super.applyConventions(project) + + // Apply default conventions FIRST (lowest priority) + exten.uid.convention(0) + exten.gid.convention(0) + exten.packageGroup.convention('java') + exten.archStr.convention('all') + exten.maintainer.convention(System.getProperty('user.name', '')) + exten.uploaders.convention('') + exten.priority.convention('optional') + + // Then apply conventions from parentExten (higher priority - override defaults) + // Only override if parentExten has a value + if (parentExten) { + if (parentExten.fileType.isPresent()) { + exten.fileType.convention(parentExten.fileType) + } + if (parentExten.uid.isPresent()) { + exten.uid.convention(parentExten.uid) + } + if (parentExten.gid.isPresent()) { + exten.gid.convention(parentExten.gid) + } + if (parentExten.packageGroup.isPresent()) { + exten.packageGroup.convention(parentExten.packageGroup) + } + if (parentExten.multiArch.isPresent()) { + exten.multiArch.convention(parentExten.multiArch) + } + if (parentExten.archStr.isPresent()) { + exten.archStr.convention(parentExten.archStr) + } + if (parentExten.maintainer.isPresent()) { + exten.maintainer.convention(parentExten.maintainer) + } + if (parentExten.uploaders.isPresent()) { + exten.uploaders.convention(parentExten.uploaders) + } + if (parentExten.priority.isPresent()) { + exten.priority.convention(parentExten.priority) + } + } } @Input @Optional List getAllRecommends() { - return getRecommends() + (parentExten?.getRecommends() ?: []) + return getRecommends() + (parentExten?.getRecommends()?.getOrElse([]) ?: []) } @Input @Optional List getAllSuggests() { - return getSuggests() + (parentExten?.getSuggests() ?: []) + return getSuggests() + (parentExten?.getSuggests()?.getOrElse([]) ?: []) } @Input @Optional List getAllEnhances() { - return getEnhances() + (parentExten?.getEnhances() ?: []) + return getEnhances() + (parentExten?.getEnhances()?.getOrElse([]) ?: []) } @Input @Optional List getAllPreDepends() { - return getPreDepends() + (parentExten?.getPreDepends() ?: []) + return getPreDepends() + (parentExten?.getPreDepends()?.getOrElse([]) ?: []) } @Input @Optional List getAllBreaks() { - return getBreaks() + (parentExten?.getBreaks() ?: []) + return getBreaks() + (parentExten?.getBreaks()?.getOrElse([]) ?: []) } @Input @Optional List getAllReplaces() { - return getReplaces() + (parentExten?.getReplaces() ?: []) + return getReplaces() + (parentExten?.getReplaces()?.getOrElse([]) ?: []) } @Input @Optional Map getAllCustomFields() { - return getCustomFields() + (parentExten?.getCustomFields() ?: [:]) + return getCustomFields() + (parentExten?.getCustomFields()?.getOrElse([:]) ?: [:]) } @OutputFile diff --git a/src/main/groovy/com/netflix/gradle/plugins/deb/DebCopyAction.groovy b/src/main/groovy/com/netflix/gradle/plugins/deb/DebCopyAction.groovy index a8a5f2ee..652a8140 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/deb/DebCopyAction.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/deb/DebCopyAction.groovy @@ -244,17 +244,17 @@ class DebCopyAction extends AbstractPackagingCopyAction { @Override protected void addDirectory(Directory directory) { - def user = directory.user ? directory.user : task.user - def permissionGroup = directory.permissionGroup ? directory.permissionGroup : task.permissionGroup + def user = directory.user ?: task.user + def permissionGroup = directory.permissionGroup ?: task.permissionGroup dataProducers << new DataProducerPathTemplate( - [directory.path] as String[], null, null, + [directory.path] as String[], null, null, [ new PermMapper(-1, -1, user, permissionGroup, directory.permissions, -1, 0, null) ] as Mapper[]) } protected String getMultiArch() { def archString = task.getArchString() - def multiArch = task.getMultiArch() + MultiArch multiArch = task.getMultiArch() as MultiArch if (('all' == archString) && (MultiArch.SAME == multiArch)) { throw new IllegalArgumentException('Deb packages with Architecture: all cannot declare Multi-Arch: same') } @@ -303,8 +303,8 @@ class DebCopyAction extends AbstractPackagingCopyAction { maintainerScriptsGenerator.generate(toContext()) - task.allSupplementaryControlFiles.each { supControl -> - File supControlFile = supControl instanceof File ? supControl as File : task.project.file(supControl) + task.allSupplementaryControlFiles.each { String supControlPath -> + File supControlFile = task.projectLayout.projectDirectory.file(supControlPath).asFile new File(debianDir, supControlFile.name).bytes = supControlFile.bytes } @@ -314,7 +314,7 @@ class DebCopyAction extends AbstractPackagingCopyAction { maker.setDeb(debFile) if (StringUtils.isNotBlank(task.getSigningKeyId()) && StringUtils.isNotBlank(task.getSigningKeyPassphrase()) - && task.getSigningKeyRingFile().exists()) { + && task.getSigningKeyRingFile()?.exists()) { maker.setKey(task.getSigningKeyId()) maker.setPassphrase(task.getSigningKeyPassphrase()) maker.setKeyring(task.getSigningKeyRingFile()) diff --git a/src/main/groovy/com/netflix/gradle/plugins/deb/DebPlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/deb/DebPlugin.groovy index 6b0b886b..674d3606 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/deb/DebPlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/deb/DebPlugin.groovy @@ -33,14 +33,11 @@ class DebPlugin implements Plugin { registerDebClass(project) // Some defaults, if not set by the user - project.tasks.withType(Deb).configureEach(new Action() { - @Override - void execute(Deb deb) { - RpmPlugin.applyAliases(deb) // RPM Specific aliases - DebPlugin.applyAliases(deb) // DEB-specific aliases - deb.applyConventions() - } - }) + project.tasks.withType(Deb).configureEach { Deb deb -> + RpmPlugin.applyAliases(deb) // RPM Specific aliases + DebPlugin.applyAliases(deb) // DEB-specific aliases + deb.applyConventions(project) + } } @CompileDynamic diff --git a/src/main/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGenerator.groovy b/src/main/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGenerator.groovy index 9c081787..835e9eb4 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGenerator.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGenerator.groovy @@ -63,7 +63,7 @@ class MaintainerScriptsGenerator { private static class MaintainerScript { String name File file - List commands + List commands boolean needsTemplateGeneration() { return commands @@ -73,7 +73,7 @@ class MaintainerScriptsGenerator { private static class PostInstScript extends MaintainerScript { Map context - PostInstScript(File file, List commands, Map context) { + PostInstScript(File file, List commands, Map context) { super("postinst", file, commands) this.context = context } diff --git a/src/main/groovy/com/netflix/gradle/plugins/docker/OsPackageDockerBasePlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/docker/OsPackageDockerBasePlugin.groovy index 2d63ee7d..a54d86b5 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/docker/OsPackageDockerBasePlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/docker/OsPackageDockerBasePlugin.groovy @@ -23,7 +23,7 @@ class OsPackageDockerBasePlugin implements Plugin { project.tasks.withType(SystemPackageDockerfile).configureEach(new Action() { @Override void execute(SystemPackageDockerfile systemPackageDockerfile) { - systemPackageDockerfile.applyConventions() + systemPackageDockerfile.applyConventions(project) } }) @@ -36,15 +36,15 @@ class OsPackageDockerBasePlugin implements Plugin { } private void createTasks(Project project) { - SystemPackageDockerfile createDockerfileTask = project.task(CREATE_DOCKERFILE_TASK_NAME, type: SystemPackageDockerfile) + def createDockerfileTaskProvider = project.tasks.register(CREATE_DOCKERFILE_TASK_NAME, SystemPackageDockerfile) - DockerBuildImage buildImageTask = project.task(BUILD_IMAGE_TASK_NAME, type: DockerBuildImage) { - dependsOn createDockerfileTask - conventionMapping.inputDir = { createDockerfileTask.destinationDir } + def buildImageTaskProvider = project.tasks.register(BUILD_IMAGE_TASK_NAME, DockerBuildImage) { + dependsOn createDockerfileTaskProvider + conventionMapping.inputDir = { createDockerfileTaskProvider.get().destinationDir } } - project.task(AGGREGATION_TASK_NAME) { - dependsOn buildImageTask + project.tasks.register(AGGREGATION_TASK_NAME) { + dependsOn buildImageTaskProvider } } } diff --git a/src/main/groovy/com/netflix/gradle/plugins/packaging/AbstractPackagingCopyAction.groovy b/src/main/groovy/com/netflix/gradle/plugins/packaging/AbstractPackagingCopyAction.groovy index 142402c6..a52934ff 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/packaging/AbstractPackagingCopyAction.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/packaging/AbstractPackagingCopyAction.groovy @@ -120,10 +120,10 @@ abstract class AbstractPackagingCopyAction implem } @CompileDynamic - String concat(Collection scripts) { + String concat(Collection scripts) { String shebang StringBuilder result = new StringBuilder(); - scripts.each { script -> + scripts.each { String script -> script?.eachLine { line -> if (line.matches('^#!.*$')) { if (!shebang) { diff --git a/src/main/groovy/com/netflix/gradle/plugins/packaging/OsPackageAbstractArchiveTask.groovy b/src/main/groovy/com/netflix/gradle/plugins/packaging/OsPackageAbstractArchiveTask.groovy index 6efa0276..4faeba73 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/packaging/OsPackageAbstractArchiveTask.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/packaging/OsPackageAbstractArchiveTask.groovy @@ -13,7 +13,6 @@ abstract class OsPackageAbstractArchiveTask extends AbstractArchiveTask { OsPackageAbstractArchiveTask() { super() - notCompatibleWithConfigurationCache("nebula.ospackage does not support configuration cache") } void setVersion(@Nullable String version) { diff --git a/src/main/groovy/com/netflix/gradle/plugins/packaging/ProjectPackagingExtension.groovy b/src/main/groovy/com/netflix/gradle/plugins/packaging/ProjectPackagingExtension.groovy index 94542f1c..a64ddc19 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/packaging/ProjectPackagingExtension.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/packaging/ProjectPackagingExtension.groovy @@ -44,6 +44,7 @@ class ProjectPackagingExtension extends SystemPackagingExtension { // @Inject // Not supported yet. @CompileDynamic ProjectPackagingExtension(Project project) { + super(project.objects, project.providers) FileResolver resolver = ((ProjectInternal) project).getFileResolver(); Instantiator instantiator = ((ProjectInternal) project).getServices().get(Instantiator.class); if (GradleVersion.current().baseVersion >= GradleVersion.version("8.13") || GradleVersion.current().version.startsWith('8.13')) { diff --git a/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePlugin.groovy index a7b295c4..589c5f00 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePlugin.groovy @@ -22,8 +22,6 @@ import com.netflix.gradle.plugins.docker.OsPackageDockerPlugin import com.netflix.gradle.plugins.rpm.RpmPlugin import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.ConventionMapping -import org.gradle.api.internal.IConventionAware import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging @@ -51,10 +49,6 @@ class SystemPackagingBasePlugin implements Plugin { ProjectPackagingExtension createExtension() { ProjectPackagingExtension extension = project.extensions.create(taskBaseName, ProjectPackagingExtension, project) - - // Ensure extension is IConventionAware - ConventionMapping mapping = ((IConventionAware) extension).getConventionMapping() - return extension } } \ No newline at end of file diff --git a/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtension.groovy b/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtension.groovy index 2432edc6..823828c8 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtension.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtension.groovy @@ -1,8 +1,15 @@ package com.netflix.gradle.plugins.packaging import com.netflix.gradle.plugins.deb.control.MultiArch +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity @@ -12,6 +19,8 @@ import org.redline_rpm.header.Os import org.redline_rpm.header.RpmType import org.redline_rpm.payload.Directive +import javax.inject.Inject + /** * Extension that can be used to configure both DEB and RPM. * @@ -36,509 +45,423 @@ class SystemPackagingExtension { static final IllegalStateException TRIGGERUNINSTALL_COMMANDS_AND_FILE_DEFINED = conflictingDefinitions('TriggerUninstall') static final IllegalStateException TRIGGERPOSTUNINSTALL_COMMANDS_AND_FILE_DEFINED = conflictingDefinitions('TriggerPostUninstall') - // File name components - String packageName - String release - String version - Integer epoch - // Package signing data - String signingKeyId - String signingKeyPassphrase - File signingKeyRingFile - String user - String permissionGroup // Group is used by Gradle on tasks. - boolean setgid - - /** - * In Debian, this is the Section and has to be provided. Valid values are: admin, cli-mono, comm, database, debug, - * devel, doc, editors, education, electronics, embedded, fonts, games, gnome, gnu-r, gnustep, graphics, hamradio, - * haskell, httpd, interpreters, introspection, java, kde, kernel, libdevel, libs, lisp, localization, mail, math, - * metapackages, misc, net, news, ocaml, oldlibs, otherosfs, perl, php, python, ruby, science, shells, sound, tasks, - * tex, text, utils, vcs, video, web, x11, xfce, zope. The section can be prefixed with contrib or non-free, if - * not part of main. - */ - String packageGroup - String buildHost - String summary - String packageDescription - String license - String packager - String distribution - String vendor - String url - String sourcePackage - // For Backward compatibility for those that passed in a Architecture object - String archStr // This is what can be convention mapped and then referenced - Directive fileType - Boolean createDirectoryEntry - Boolean addParentDirs - Os os - RpmType type - List prefixes = new ArrayList() - // DEB Only - Integer uid - Integer gid - MultiArch multiArch - String maintainer - String uploaders - String priority - File preInstallFile - File postInstallFile - File preUninstallFile - File postUninstallFile - File triggerInstallFile - File triggerUninstallFile - File triggerPostUninstallFile - - final List configurationFiles = [] - final List preInstallCommands = [] - final List postInstallCommands = [] - final List preUninstallCommands = [] - final List postUninstallCommands = [] - - // RPM specific - final List triggerInstallCommands = [] - final List triggerUninstallCommands = [] - final List triggerPostUninstallCommands = [] - final List preTransCommands = [] - final List postTransCommands = [] - final List commonCommands = [] - - /** - * Can be of type String or File - */ - final List supplementaryControlFiles = [] - - List links = new ArrayList() - List dependencies = new ArrayList() - List obsoletes = new ArrayList() - List conflicts = new ArrayList() - // Deb-specific special dependencies - List recommends = new ArrayList() - List suggests = new ArrayList() - List enhances = new ArrayList() - List preDepends = new ArrayList() - List breaks = new ArrayList() - List replaces = new ArrayList() - List provides = new ArrayList() - List directories = new ArrayList() - - // DEB-specific user-defined fields - // https://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.7 - Map customFields = [:] - - @Input - @Optional - String getPackageName() { - return packageName - } + private final ObjectFactory objects + private final ProviderFactory providers - @Input - @Optional - String getRelease() { - return release - } + // Track when commands are added via File (Provider) - needed for validation + private boolean hasPreInstallCommands = false + private boolean hasPostInstallCommands = false + private boolean hasPreUninstallCommands = false + private boolean hasPostUninstallCommands = false - @Input - @Optional - String getVersion() { - return version + @Inject + SystemPackagingExtension(ObjectFactory objects, ProviderFactory providers) { + this.objects = objects + this.providers = providers } - @Input - @Optional - Integer getEpoch() { - return epoch + @Internal + ObjectFactory getObjects() { + return objects } + // Core String Properties @Input @Optional - String getSigningKeyId() { - return signingKeyId - } + final Property packageName = objects.property(String) @Input @Optional - String getSigningKeyPassphrase() { - return signingKeyPassphrase - } - - @InputFile - @Optional - @PathSensitive(PathSensitivity.ABSOLUTE) - File getSigningKeyRingFile() { - return signingKeyRingFile - } + final Property release = objects.property(String) @Input @Optional - String getUser() { - return user - } + final Property version = objects.property(String) @Input @Optional - String getPermissionGroup() { - return permissionGroup - } + final Property user = objects.property(String) @Input @Optional - Boolean getSetgid() { - return setgid - } + final Property permissionGroup = objects.property(String) + /** + * In Debian, this is the Section and has to be provided. Valid values are: admin, cli-mono, comm, database, debug, + * devel, doc, editors, education, electronics, embedded, fonts, games, gnome, gnu-r, gnustep, graphics, hamradio, + * haskell, httpd, interpreters, introspection, java, kde, kernel, libdevel, libs, lisp, localization, mail, math, + * metapackages, misc, net, news, ocaml, oldlibs, otherosfs, perl, php, python, ruby, science, shells, sound, tasks, + * tex, text, utils, vcs, video, web, x11, xfce, zope. The section can be prefixed with contrib or non-free, if + * not part of main. + */ @Input @Optional - String getPackageGroup() { - return packageGroup - } + final Property packageGroup = objects.property(String) @Input @Optional - String getBuildHost() { - return buildHost - } + final Property buildHost = objects.property(String) @Input @Optional - String getSummary() { - return summary - } + final Property summary = objects.property(String) @Input @Optional - String getPackageDescription() { - return packageDescription - } + final Property packageDescription = objects.property(String) @Input @Optional - String getLicense() { - return license - } + final Property license = objects.property(String) @Input @Optional - String getPackager() { - return packager - } + final Property packager = objects.property(String) @Input @Optional - String getDistribution() { - return distribution - } + final Property distribution = objects.property(String) @Input @Optional - String getVendor() { - return vendor - } + final Property vendor = objects.property(String) @Input @Optional - String getUrl() { - return url - } + final Property url = objects.property(String) @Input @Optional - String getSourcePackage() { - return sourcePackage - } + final Property sourcePackage = objects.property(String) @Input @Optional - String getArchStr() { - return archStr - } - - void setArch(Object arch) { - archStr = (arch instanceof Architecture) ? arch.name() : arch.toString() - } + final Property archStr = objects.property(String) @Input @Optional - Directive getFileType() { - return fileType - } + final Property maintainer = objects.property(String) @Input @Optional - Boolean getCreateDirectoryEntry() { - return createDirectoryEntry - } + final Property uploaders = objects.property(String) @Input @Optional - Boolean getAddParentDirs() { - return addParentDirs - } + final Property priority = objects.property(String) + // Integer Properties @Input @Optional - Os getOs() { - return os - } + final Property epoch = objects.property(Integer) @Input @Optional - RpmType getType() { - return type - } - - - def prefix(String prefixStr) { - prefixes << prefixStr - return this - } + final Property uid = objects.property(Integer) @Input @Optional - List getPrefixes() { - return prefixes - } + final Property gid = objects.property(Integer) + // Boolean Properties @Input @Optional - Integer getUid() { - return uid - } + final Property setgid = objects.property(Boolean) @Input @Optional - Integer getGid() { - return gid - } + final Property createDirectoryEntry = objects.property(Boolean) @Input @Optional - MultiArch getMultiArch() { - return multiArch - } + final Property addParentDirs = objects.property(Boolean) + // Package Signing Properties @Input @Optional - String getMaintainer() { - return maintainer - } + final Property signingKeyId = objects.property(String) @Input @Optional - String getUploaders() { - return uploaders - } + final Property signingKeyPassphrase = objects.property(String) - @Input - @Optional - String getPriority() { - return priority - } - - @Input + @InputFile @Optional - List getSupplementaryControlFiles() { - return supplementaryControlFiles - } - - def supplementaryControl(Object file) { - supplementaryControlFiles << file - return this - } + @PathSensitive(PathSensitivity.ABSOLUTE) + final RegularFileProperty signingKeyRingFile = objects.fileProperty() - // Scripts + // Script Files @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getPreInstallFile() { - return preInstallFile - } + final RegularFileProperty preInstallFile = objects.fileProperty() @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getPostInstallFile() { - return postInstallFile - } + final RegularFileProperty postInstallFile = objects.fileProperty() @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getPreUninstallFile() { - return preUninstallFile - } + final RegularFileProperty preUninstallFile = objects.fileProperty() @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getPostUninstallFile() { - return postUninstallFile - } + final RegularFileProperty postUninstallFile = objects.fileProperty() @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getTriggerInstallFile() { - return triggerInstallFile - } + final RegularFileProperty triggerInstallFile = objects.fileProperty() @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getTriggerUninstallFile() { - return triggerUninstallFile - } + final RegularFileProperty triggerUninstallFile = objects.fileProperty() @InputFile @Optional @PathSensitive(PathSensitivity.RELATIVE) - File getTriggerPostUninstallFile() { - return triggerPostUninstallFile - } + final RegularFileProperty triggerPostUninstallFile = objects.fileProperty() + // Enum Properties @Input @Optional - List getConfigurationFiles() { - return configurationFiles - } + final Property fileType = objects.property(Directive) @Input @Optional - List getPreInstallCommands() { - return preInstallCommands - } + final Property os = objects.property(Os) @Input @Optional - List getPostInstallCommands() { - return postInstallCommands - } + final Property type = objects.property(RpmType) @Input @Optional - List getPreUninstallCommands() { - return preUninstallCommands - } + final Property multiArch = objects.property(MultiArch) + // List Properties @Input @Optional - List getPostUninstallCommands() { - return postUninstallCommands - } + final ListProperty prefixes = objects.listProperty(String) @Input @Optional - List getTriggerInstallCommands() { - return triggerInstallCommands - } + final ListProperty configurationFiles = objects.listProperty(String) @Input @Optional - List getTriggerUninstallCommands() { - return triggerUninstallCommands - } + final ListProperty preInstallCommands = objects.listProperty(String) @Input @Optional - List getTriggerPostUninstallCommands() { - return triggerPostUninstallCommands - } + final ListProperty postInstallCommands = objects.listProperty(String) @Input @Optional - List getPreTransCommands() { - return preTransCommands - } + final ListProperty preUninstallCommands = objects.listProperty(String) @Input @Optional - List getPostTransCommands() { - return postTransCommands - } + final ListProperty postUninstallCommands = objects.listProperty(String) @Input @Optional - List getCommonCommands() { - return commonCommands - } + final ListProperty triggerInstallCommands = objects.listProperty(Trigger) @Input @Optional - List getLinks() { - return links - } + final ListProperty triggerUninstallCommands = objects.listProperty(Trigger) @Input @Optional - List getDependencies() { - return dependencies - } + final ListProperty triggerPostUninstallCommands = objects.listProperty(Trigger) @Input @Optional - List getObsoletes() { - return obsoletes - } + final ListProperty preTransCommands = objects.listProperty(String) @Input @Optional - List getRecommends() { - return recommends - } + final ListProperty postTransCommands = objects.listProperty(String) @Input @Optional - List getSuggests() { - return suggests - } + final ListProperty commonCommands = objects.listProperty(String) @Input @Optional - List getEnhances() { - return enhances - } + final ListProperty supplementaryControlFiles = objects.listProperty(String) @Input @Optional - List getPreDepends() { - return preDepends - } + final ListProperty links = objects.listProperty(Link) @Input @Optional - List getBreaks() { - return breaks - } + final ListProperty dependencies = objects.listProperty(Dependency) @Input @Optional - List getReplaces() { - return replaces - } + final ListProperty obsoletes = objects.listProperty(Dependency) @Input @Optional - List getProvides() { - return provides - } + final ListProperty conflicts = objects.listProperty(Dependency) @Input @Optional - List getConflicts() { - return conflicts - } + final ListProperty recommends = objects.listProperty(Dependency) @Input @Optional - List getDirectories() { - return directories - } + final ListProperty suggests = objects.listProperty(Dependency) + + @Input + @Optional + final ListProperty enhances = objects.listProperty(Dependency) + + @Input + @Optional + final ListProperty preDepends = objects.listProperty(Dependency) + + @Input + @Optional + final ListProperty breaks = objects.listProperty(Dependency) + + @Input + @Optional + final ListProperty replaces = objects.listProperty(Dependency) + + @Input + @Optional + final ListProperty provides = objects.listProperty(Dependency) @Input @Optional - Map getCustomFields() { - return customFields + final ListProperty directories = objects.listProperty(Directory) + + // Map Property + @Input + @Optional + final MapProperty customFields = objects.mapProperty(String, String) + + // Getter methods to maintain API compatibility + Property getPackageName() { packageName } + Property getRelease() { release } + Property getVersion() { version } + Property getUser() { user } + Property getPermissionGroup() { permissionGroup } + Property getPackageGroup() { packageGroup } + Property getBuildHost() { buildHost } + Property getSummary() { summary } + Property getPackageDescription() { packageDescription } + Property getLicense() { license } + Property getPackager() { packager } + Property getDistribution() { distribution } + Property getVendor() { vendor } + Property getUrl() { url } + Property getSourcePackage() { sourcePackage } + Property getArchStr() { archStr } + Property getMaintainer() { maintainer } + Property getUploaders() { uploaders } + Property getPriority() { priority } + Property getEpoch() { epoch } + Property getUid() { uid } + Property getGid() { gid } + Property getSetgid() { setgid } + Property getCreateDirectoryEntry() { createDirectoryEntry } + Property getAddParentDirs() { addParentDirs } + Property getSigningKeyId() { signingKeyId } + Property getSigningKeyPassphrase() { signingKeyPassphrase } + RegularFileProperty getSigningKeyRingFile() { signingKeyRingFile } + RegularFileProperty getPreInstallFile() { preInstallFile } + RegularFileProperty getPostInstallFile() { postInstallFile } + RegularFileProperty getPreUninstallFile() { preUninstallFile } + RegularFileProperty getPostUninstallFile() { postUninstallFile } + RegularFileProperty getTriggerInstallFile() { triggerInstallFile } + RegularFileProperty getTriggerUninstallFile() { triggerUninstallFile } + RegularFileProperty getTriggerPostUninstallFile() { triggerPostUninstallFile } + Property getFileType() { fileType } + Property getOs() { os } + Property getType() { type } + Property getMultiArch() { multiArch } + ListProperty getPrefixes() { prefixes } + ListProperty getConfigurationFiles() { configurationFiles } + ListProperty getPreInstallCommands() { preInstallCommands } + ListProperty getPostInstallCommands() { postInstallCommands } + ListProperty getPreUninstallCommands() { preUninstallCommands } + ListProperty getPostUninstallCommands() { postUninstallCommands } + ListProperty getTriggerInstallCommands() { triggerInstallCommands } + ListProperty getTriggerUninstallCommands() { triggerUninstallCommands } + ListProperty getTriggerPostUninstallCommands() { triggerPostUninstallCommands } + ListProperty getPreTransCommands() { preTransCommands } + ListProperty getPostTransCommands() { postTransCommands } + ListProperty getCommonCommands() { commonCommands } + ListProperty getSupplementaryControlFiles() { supplementaryControlFiles } + ListProperty getLinks() { links } + ListProperty getDependencies() { dependencies } + ListProperty getObsoletes() { obsoletes } + ListProperty getConflicts() { conflicts } + ListProperty getRecommends() { recommends } + ListProperty getSuggests() { suggests } + ListProperty getEnhances() { enhances } + ListProperty getPreDepends() { preDepends } + ListProperty getBreaks() { breaks } + ListProperty getReplaces() { replaces } + ListProperty getProvides() { provides } + ListProperty getDirectories() { directories } + MapProperty getCustomFields() { customFields } + + // Convenience methods for backward compatibility (v13.0 recommended API design) + + void packageName(String value) { packageName.set(value) } + void release(String value) { release.set(value) } + void version(String value) { version.set(value) } + void user(String value) { user.set(value) } + void permissionGroup(String value) { permissionGroup.set(value) } + void packageGroup(String value) { packageGroup.set(value) } + void buildHost(String value) { buildHost.set(value) } + void summary(String value) { summary.set(value) } + void packageDescription(String value) { packageDescription.set(value) } + void license(String value) { license.set(value) } + void packager(String value) { packager.set(value) } + void distribution(String value) { distribution.set(value) } + void vendor(String value) { vendor.set(value) } + void url(String value) { url.set(value) } + void sourcePackage(String value) { sourcePackage.set(value) } + void maintainer(String value) { maintainer.set(value) } + void uploaders(String value) { uploaders.set(value) } + void priority(String value) { priority.set(value) } + void epoch(Integer value) { epoch.set(value) } + void uid(Integer value) { uid.set(value) } + void gid(Integer value) { gid.set(value) } + void setgid(Boolean value) { setgid.set(value) } + void createDirectoryEntry(Boolean value) { createDirectoryEntry.set(value) } + void addParentDirs(Boolean value) { addParentDirs.set(value) } + void signingKeyId(String value) { signingKeyId.set(value) } + void signingKeyPassphrase(String value) { signingKeyPassphrase.set(value) } + void fileType(Object value) { fileType.set(value as Directive) } + void os(Object value) { os.set(value as Os) } + void type(Object value) { type.set(value as RpmType) } + void multiArch(Object value) { multiArch.set(value as MultiArch) } + + void setArch(Object arch) { + archStr.set((arch instanceof Architecture) ? arch.name() : arch.toString()) + } + + def prefix(String prefixStr) { + prefixes.add(prefixStr) + return this + } + + def supplementaryControl(Object file) { + // Convert File to path String, keep String as-is + supplementaryControlFiles.add(file instanceof File ? ((File)file).path : file.toString()) + return this } /** @@ -550,12 +473,15 @@ class SystemPackagingExtension { } def installUtils(String script) { - commonCommands << script + commonCommands.add(script) return this } def installUtils(File script) { - commonCommands << script + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + commonCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) return this } @@ -567,8 +493,9 @@ class SystemPackagingExtension { configurationFile(script) } - def configurationFile(String path) { - configurationFiles << path + def configurationFile(Object path) { + // Convert File to path String, keep String as-is + configurationFiles.add(path instanceof File ? ((File)path).path : path.toString()) return this } @@ -581,21 +508,27 @@ class SystemPackagingExtension { } def preInstall(String script) { - if(preInstallFile) { throw PREINSTALL_COMMANDS_AND_FILE_DEFINED } - preInstallCommands << script + if(preInstallFile.isPresent()) { throw PREINSTALL_COMMANDS_AND_FILE_DEFINED } + preInstallCommands.add(script) return this } def preInstall(File script) { - if(preInstallFile) { throw PREINSTALL_COMMANDS_AND_FILE_DEFINED } - preInstallCommands << script + if(preInstallFile.isPresent()) { throw PREINSTALL_COMMANDS_AND_FILE_DEFINED } + // Use providers.fileContents() for config-cache-safe file reading at execution time + // This preserves the original behavior: file read at execution, not configuration + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + preInstallCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) + hasPreInstallCommands = true return this } def preInstallFile(File path) { - if(preInstallFile) { throw MULTIPLE_PREINSTALL_FILES } - if(preInstallCommands) { throw PREINSTALL_COMMANDS_AND_FILE_DEFINED } - preInstallFile = path + if(preInstallFile.isPresent()) { throw MULTIPLE_PREINSTALL_FILES } + if(hasPreInstallCommands || !preInstallCommands.getOrElse([]).isEmpty()) { throw PREINSTALL_COMMANDS_AND_FILE_DEFINED } + preInstallFile.set(path) } /** @@ -607,21 +540,25 @@ class SystemPackagingExtension { } def postInstall(String script) { - if(postInstallFile) { throw POSTINSTALL_COMMANDS_AND_FILE_DEFINED } - postInstallCommands << script + if(postInstallFile.isPresent()) { throw POSTINSTALL_COMMANDS_AND_FILE_DEFINED } + postInstallCommands.add(script) return this } def postInstall(File script) { - if(postInstallFile) { throw POSTINSTALL_COMMANDS_AND_FILE_DEFINED } - postInstallCommands << script + if(postInstallFile.isPresent()) { throw POSTINSTALL_COMMANDS_AND_FILE_DEFINED } + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + postInstallCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) + hasPostInstallCommands = true return this } def postInstallFile(File path) { - if(postInstallFile) { throw MULTIPLE_POSTINSTALL_FILES } - if(postInstallCommands) { throw POSTINSTALL_COMMANDS_AND_FILE_DEFINED } - postInstallFile = path + if(postInstallFile.isPresent()) { throw MULTIPLE_POSTINSTALL_FILES } + if(hasPostInstallCommands || !postInstallCommands.getOrElse([]).isEmpty()) { throw POSTINSTALL_COMMANDS_AND_FILE_DEFINED } + postInstallFile.set(path) } /** @@ -633,21 +570,25 @@ class SystemPackagingExtension { } def preUninstall(String script) { - if(preUninstallFile) { throw PREUNINSTALL_COMMANDS_AND_FILE_DEFINED } - preUninstallCommands << script + if(preUninstallFile.isPresent()) { throw PREUNINSTALL_COMMANDS_AND_FILE_DEFINED } + preUninstallCommands.add(script) return this } def preUninstall(File script) { - if(preUninstallFile) { throw PREUNINSTALL_COMMANDS_AND_FILE_DEFINED } - preUninstallCommands << script + if(preUninstallFile.isPresent()) { throw PREUNINSTALL_COMMANDS_AND_FILE_DEFINED } + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + preUninstallCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) + hasPreUninstallCommands = true return this } def preUninstallFile(File script) { - if(preUninstallFile) { throw MULTIPLE_PREUNINSTALL_FILES } - if(preUninstallCommands) { throw PREUNINSTALL_COMMANDS_AND_FILE_DEFINED } - preUninstallFile = script + if(preUninstallFile.isPresent()) { throw MULTIPLE_PREUNINSTALL_FILES } + if(hasPreUninstallCommands || !preUninstallCommands.getOrElse([]).isEmpty()) { throw PREUNINSTALL_COMMANDS_AND_FILE_DEFINED } + preUninstallFile.set(script) } /** @@ -659,21 +600,25 @@ class SystemPackagingExtension { } def postUninstall(String script) { - if(postUninstallFile) { throw POSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } - postUninstallCommands << script + if(postUninstallFile.isPresent()) { throw POSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } + postUninstallCommands.add(script) return this } def postUninstall(File script) { - if(postUninstallFile) { throw POSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } - postUninstallCommands << script + if(postUninstallFile.isPresent()) { throw POSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + postUninstallCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) + hasPostUninstallCommands = true return this } def postUninstallFile(File script) { - if(postUninstallFile) { throw MULTIPLE_POSTUNINSTALL_FILES } - if(postUninstallCommands) { throw POSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } - postUninstallFile = script + if(postUninstallFile.isPresent()) { throw MULTIPLE_POSTUNINSTALL_FILES } + if(hasPostUninstallCommands || !postUninstallCommands.getOrElse([]).isEmpty()) { throw POSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } + postUninstallFile.set(script) } /** @@ -685,15 +630,15 @@ class SystemPackagingExtension { } def triggerInstall(File script, String packageName, String version='', int flag=0) { - if(triggerInstallFile) { throw TRIGGERINSTALL_COMMANDS_AND_FILE_DEFINED } - triggerInstallCommands << new Trigger(new Dependency(packageName, version, flag), script) + if(triggerInstallFile.isPresent()) { throw TRIGGERINSTALL_COMMANDS_AND_FILE_DEFINED } + triggerInstallCommands.add(new Trigger(new Dependency(packageName, version, flag), script)) return this } def triggerInstallFile(File script) { - if(triggerInstallFile) { throw MULTIPLE_TRIGGERINSTALL_FILES } - if(triggerInstallCommands) { throw TRIGGERINSTALL_COMMANDS_AND_FILE_DEFINED } - triggerInstallFile = script + if(triggerInstallFile.isPresent()) { throw MULTIPLE_TRIGGERINSTALL_FILES } + if(!triggerInstallCommands.get().isEmpty()) { throw TRIGGERINSTALL_COMMANDS_AND_FILE_DEFINED } + triggerInstallFile.set(script) } /** @@ -705,15 +650,15 @@ class SystemPackagingExtension { } def triggerUninstall(File script, String packageName, String version='', int flag=0) { - if(triggerUninstallFile) { throw TRIGGERUNINSTALL_COMMANDS_AND_FILE_DEFINED } - triggerUninstallCommands << new Trigger(new Dependency(packageName, version, flag), script) + if(triggerUninstallFile.isPresent()) { throw TRIGGERUNINSTALL_COMMANDS_AND_FILE_DEFINED } + triggerUninstallCommands.add(new Trigger(new Dependency(packageName, version, flag), script)) return this } def triggerUninstallFile(File script) { - if(triggerUninstallFile) { throw MULTIPLE_TRIGGERUNINSTALL_FILES } - if(triggerUninstallCommands) { throw TRIGGERUNINSTALL_COMMANDS_AND_FILE_DEFINED } - triggerUninstallFile = script + if(triggerUninstallFile.isPresent()) { throw MULTIPLE_TRIGGERUNINSTALL_FILES } + if(!triggerUninstallCommands.get().isEmpty()) { throw TRIGGERUNINSTALL_COMMANDS_AND_FILE_DEFINED } + triggerUninstallFile.set(script) } /** @@ -725,15 +670,15 @@ class SystemPackagingExtension { } def triggerPostUninstall(File script, String packageName, String version='', int flag=0) { - if(triggerPostUninstallFile) { throw TRIGGERPOSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } - triggerPostUninstallCommands << new Trigger(new Dependency(packageName, version, flag), script) + if(triggerPostUninstallFile.isPresent()) { throw TRIGGERPOSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } + triggerPostUninstallCommands.add(new Trigger(new Dependency(packageName, version, flag), script)) return this } def triggerPostUninstallFile(File script) { - if(triggerPostUninstallFile) { throw MULTIPLE_TRIGGERPOSTUNINSTALL_FILES } - if(triggerUninstallCommands) { throw TRIGGERPOSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } - triggerPostUninstallFile = script + if(triggerPostUninstallFile.isPresent()) { throw MULTIPLE_TRIGGERPOSTUNINSTALL_FILES } + if(!triggerUninstallCommands.get().isEmpty()) { throw TRIGGERPOSTUNINSTALL_COMMANDS_AND_FILE_DEFINED } + triggerPostUninstallFile.set(script) } /** @@ -745,12 +690,15 @@ class SystemPackagingExtension { } def preTrans(String script) { - preTransCommands << script + preTransCommands.add(script) return this } def preTrans(File script) { - preTransCommands << script + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + preTransCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) return this } @@ -763,19 +711,20 @@ class SystemPackagingExtension { } def postTrans(String script) { - postTransCommands << script + postTransCommands.add(script) return this } def postTrans(File script) { - postTransCommands << script + def fileProperty = objects.fileProperty() + fileProperty.set(script) + def contentProvider = providers.fileContents(fileProperty).asText.orElse('') + postTransCommands.addAll(contentProvider.map { content -> content ? [content] : [] }) return this } // @groovy.transform.PackageScope doesn't seem to set the proper scope when going through a @Delegate - - Link link(String path, String target) { link(path, target, -1, null, null) } @@ -799,7 +748,6 @@ class SystemPackagingExtension { link } - Dependency requires(String packageName, String version, int flag) { def dep = new Dependency(packageName, version, flag) dependencies.add(dep) @@ -908,50 +856,49 @@ class SystemPackagingExtension { provides(packageName, '', 0) } - Directory directory(String path) { Directory directory = directory(path, -1) - directories << directory + directories.add(directory) directory } Directory directory(String path, boolean addParents) { Directory directory = new Directory(path: path, addParents: addParents) - directories << directory + directories.add(directory) directory } Directory directory(String path, int permissions) { Directory directory = new Directory(path: path, permissions: permissions) - directories << directory + directories.add(directory) directory } Directory directory(String path, int permissions, boolean addParents) { Directory directory = new Directory(path: path, permissions: permissions, addParents: addParents) - directories << directory + directories.add(directory) directory } Directory directory(String path, int permissions, String user, String permissionGroup) { Directory directory = new Directory(path: path, permissions: permissions, user: user, permissionGroup: permissionGroup) - directories << directory + directories.add(directory) directory } Directory directory(String path, int permissions, String user, String permissionGroup, boolean addParents) { Directory directory = new Directory(path: path, permissions: permissions, user: user, permissionGroup: permissionGroup, addParents: addParents) - directories << directory + directories.add(directory) directory } def customField(String key, String val) { - customFields[key] = val + customFields.put(key, val) return this } def customField(Map fields) { - customFields += fields + customFields.putAll(fields) return this } diff --git a/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingTask.groovy b/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingTask.groovy index d356580f..fcb7a7ec 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingTask.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingTask.groovy @@ -18,35 +18,38 @@ package com.netflix.gradle.plugins.packaging import com.netflix.gradle.plugins.utils.DeprecationLoggerUtils import groovy.transform.CompileDynamic +import org.gradle.api.Project import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileCollection import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFile -import org.gradle.api.internal.ConventionMapping -import org.gradle.api.internal.IConventionAware import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionExecuter import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.* import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.util.GradleVersion import org.gradle.work.DisableCachingByDefault import org.redline_rpm.header.Architecture -import org.gradle.api.provider.Property +import javax.inject.Inject import java.util.concurrent.Callable @DisableCachingByDefault abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { private static final String HOST_NAME = getLocalHostName() - @Internal - final ObjectFactory objectFactory = project.objects + @Inject + abstract ObjectFactory getObjectFactory() + + @Inject + abstract ProviderFactory getProviders() - @Delegate(methodAnnotations = true) @Nested - SystemPackagingExtension exten // Not File extension or ext list of properties, different kind of Extension + abstract SystemPackagingExtension getExten() // Not File extension or ext list of properties, different kind of Extension @Internal ProjectPackagingExtension parentExten @@ -57,7 +60,7 @@ abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { // TODO Add conventions to pull from extension SystemPackagingTask(ProjectLayout projectLayout) { super() - exten = new SystemPackagingExtension() + // exten is now an abstract getter - Gradle will instantiate it this.projectLayout = projectLayout // I have no idea where Project came from parentExten = project.extensions.findByType(ProjectPackagingExtension) @@ -66,7 +69,6 @@ abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { } configureDuplicateStrategy() - notCompatibleWithConfigurationCache("nebula.ospackage does not support configuration cache") } private void configureDuplicateStrategy() { @@ -74,82 +76,495 @@ abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { rootSpec.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) mainSpec.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) } - /** - * This should go into SystemPackagingExtension, but if we do, we won't be interacting correctly with the convention mapping. - * @param arch - */ + + // Explicit delegation methods (replacing @Delegate) + // These provide backward-compatible plain value access while using Property API internally + + @Input + @Optional + String getPackageName() { getExten().getPackageName().getOrNull() } + void setPackageName(String value) { getExten().packageName(value) } + void packageName(String value) { setPackageName(value) } + + @Input + @Optional + String getRelease() { getExten().getRelease().getOrNull() } + void setRelease(String value) { getExten().release(value) } + void release(String value) { setRelease(value) } + + @Input + @Optional + String getVersion() { getExten().getVersion().getOrNull() } + void setVersion(String value) { getExten().version(value) } + void version(String value) { setVersion(value) } + + @Input + @Optional + String getUser() { getExten().getUser().getOrNull() } + void setUser(String value) { getExten().user(value) } + void user(String value) { setUser(value) } + + @Input + @Optional + String getPermissionGroup() { getExten().getPermissionGroup().getOrNull() } + void setPermissionGroup(String value) { getExten().permissionGroup(value) } + void permissionGroup(String value) { setPermissionGroup(value) } + + @Input + @Optional + String getPackageGroup() { getExten().getPackageGroup().getOrNull() } + void setPackageGroup(String value) { getExten().packageGroup(value) } + void packageGroup(String value) { setPackageGroup(value) } + + @Input + @Optional + String getBuildHost() { getExten().getBuildHost().getOrNull() } + void setBuildHost(String value) { getExten().buildHost(value) } + void buildHost(String value) { setBuildHost(value) } + + @Input + @Optional + String getSummary() { getExten().getSummary().getOrNull() } + void setSummary(String value) { getExten().summary(value) } + void summary(String value) { setSummary(value) } + + @Input + @Optional + String getPackageDescription() { getExten().getPackageDescription().getOrNull() } + void setPackageDescription(String value) { getExten().packageDescription(value) } + void packageDescription(String value) { setPackageDescription(value) } + + @Input + @Optional + String getLicense() { getExten().getLicense().getOrNull() } + void setLicense(String value) { getExten().license(value) } + void license(String value) { setLicense(value) } + + @Input + @Optional + String getPackager() { getExten().getPackager().getOrNull() } + void setPackager(String value) { getExten().packager(value) } + void packager(String value) { setPackager(value) } + + @Input + @Optional + String getDistribution() { getExten().getDistribution().getOrNull() } + void setDistribution(String value) { getExten().distribution(value) } + void distribution(String value) { setDistribution(value) } + + @Input + @Optional + String getVendor() { getExten().getVendor().getOrNull() } + void setVendor(String value) { getExten().vendor(value) } + void vendor(String value) { setVendor(value) } + + @Input + @Optional + String getUrl() { getExten().getUrl().getOrNull() } + void setUrl(String value) { getExten().url(value) } + void url(String value) { setUrl(value) } + + @Input + @Optional + String getSourcePackage() { getExten().getSourcePackage().getOrNull() } + void setSourcePackage(String value) { getExten().sourcePackage(value) } + void sourcePackage(String value) { setSourcePackage(value) } + + @Input + @Optional + String getArchStr() { getExten().getArchStr().getOrNull() } + void setArchStr(String value) { getExten().getArchStr().set(value) } + void setArch(Object arch) { setArchStr((arch instanceof Architecture) ? arch.name() : arch.toString()) } - // TODO Move outside task, since it's specific to a plugin - protected void applyConventions() { - // For all mappings, we're only being called if it wasn't explicitly set on the task. In which case, we'll want - // to pull from the parentExten. And only then would we fallback on some other value. + @Input + @Optional + String getMaintainer() { getExten().getMaintainer().getOrNull() } + void setMaintainer(String value) { getExten().maintainer(value) } + void maintainer(String value) { setMaintainer(value) } - ConventionMapping mapping = ((IConventionAware) this).getConventionMapping() + @Input + @Optional + String getUploaders() { getExten().getUploaders().getOrNull() } + void setUploaders(String value) { getExten().uploaders(value) } + void uploaders(String value) { setUploaders(value) } - DeprecationLoggerUtils.whileDisabled { - // Could come from extension - mapping.map('packageName', { - // BasePlugin defaults this to pluginConvention.getArchivesBaseName(), which in turns comes form project.name - parentExten?.getPackageName() ?: getArchiveBaseName().getOrNull() - }) - mapping.map('release', { parentExten?.getRelease() ?: getArchiveClassifier().getOrNull() }) - mapping.map('version', { sanitizeVersion() }) - mapping.map('epoch', { parentExten?.getEpoch() ?: 0 }) - mapping.map('signingKeyId', { parentExten?.getSigningKeyId() ?: '' }) - mapping.map('signingKeyPassphrase', { parentExten?.getSigningKeyPassphrase() ?: '' }) - mapping.map('signingKeyRingFile', { - File defaultFile = new File(System.getProperty('user.home').toString(), '.gnupg/secring.gpg') - parentExten?.getSigningKeyRingFile() ?: (defaultFile.exists() ? defaultFile : null) - }) - mapping.map('user', { parentExten?.getUser() ?: getPackager() }) - mapping.map('maintainer', { parentExten?.getMaintainer() ?: getPackager() }) - mapping.map('uploaders', { parentExten?.getUploaders() ?: getPackager() }) - mapping.map('permissionGroup', { parentExten?.getPermissionGroup() ?: '' }) - mapping.map('setgid', { parentExten?.getSetgid() ?: false }) - mapping.map('packageGroup', { parentExten?.getPackageGroup() }) - mapping.map('buildHost', { parentExten?.getBuildHost() ?: HOST_NAME }) - mapping.map('summary', { parentExten?.getSummary() ?: getPackageName() }) - mapping.map('packageDescription', { - String packageDescription = parentExten?.getPackageDescription() ?: project.getDescription() - packageDescription ?: '' + @Input + @Optional + String getPriority() { getExten().getPriority().getOrNull() } + void setPriority(String value) { getExten().priority(value) } + void priority(String value) { setPriority(value) } + + @Input + @Optional + Integer getEpoch() { getExten().getEpoch().getOrNull() } + void setEpoch(Integer value) { getExten().epoch(value) } + void epoch(Integer value) { setEpoch(value) } + + @Input + @Optional + Integer getUid() { getExten().getUid().getOrNull() } + void setUid(Integer value) { getExten().uid(value) } + void uid(Integer value) { setUid(value) } + + @Input + @Optional + Integer getGid() { getExten().getGid().getOrNull() } + void setGid(Integer value) { getExten().gid(value) } + void gid(Integer value) { setGid(value) } + + @Input + @Optional + Boolean getSetgid() { getExten().getSetgid().getOrNull() } + void setSetgid(Boolean value) { getExten().setgid(value) } + // Note: No convenience method due to naming collision with setter + + @Input + @Optional + Boolean getCreateDirectoryEntry() { getExten().getCreateDirectoryEntry().getOrNull() } + void setCreateDirectoryEntry(Boolean value) { getExten().createDirectoryEntry(value) } + void createDirectoryEntry(Boolean value) { setCreateDirectoryEntry(value) } + + @Input + @Optional + Boolean getAddParentDirs() { getExten().getAddParentDirs().getOrNull() } + void setAddParentDirs(Boolean value) { getExten().addParentDirs(value) } + void addParentDirs(Boolean value) { setAddParentDirs(value) } + + @Input + @Optional + String getSigningKeyId() { getExten().getSigningKeyId().getOrNull() } + void setSigningKeyId(String value) { getExten().signingKeyId(value) } + void signingKeyId(String value) { setSigningKeyId(value) } + + @Input + @Optional + String getSigningKeyPassphrase() { getExten().getSigningKeyPassphrase().getOrNull() } + void setSigningKeyPassphrase(String value) { getExten().signingKeyPassphrase(value) } + void signingKeyPassphrase(String value) { setSigningKeyPassphrase(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.ABSOLUTE) + File getSigningKeyRingFile() { getExten().getSigningKeyRingFile().getOrNull()?.asFile } + void setSigningKeyRingFile(File value) { getExten().getSigningKeyRingFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getPreInstallFile() { getExten().getPreInstallFile().getOrNull()?.asFile } + void setPreInstallFile(File value) { getExten().getPreInstallFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getPostInstallFile() { getExten().getPostInstallFile().getOrNull()?.asFile } + void setPostInstallFile(File value) { getExten().getPostInstallFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getPreUninstallFile() { getExten().getPreUninstallFile().getOrNull()?.asFile } + void setPreUninstallFile(File value) { getExten().getPreUninstallFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getPostUninstallFile() { getExten().getPostUninstallFile().getOrNull()?.asFile } + void setPostUninstallFile(File value) { getExten().getPostUninstallFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getTriggerInstallFile() { getExten().getTriggerInstallFile().getOrNull()?.asFile } + void setTriggerInstallFile(File value) { getExten().getTriggerInstallFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getTriggerUninstallFile() { getExten().getTriggerUninstallFile().getOrNull()?.asFile } + void setTriggerUninstallFile(File value) { getExten().getTriggerUninstallFile().set(value) } + + @InputFile + @Optional + @PathSensitive(PathSensitivity.RELATIVE) + File getTriggerPostUninstallFile() { getExten().getTriggerPostUninstallFile().getOrNull()?.asFile } + void setTriggerPostUninstallFile(File value) { getExten().getTriggerPostUninstallFile().set(value) } + + @Input + @Optional + def getFileType() { getExten().getFileType().getOrNull() } + void setFileType(def value) { getExten().fileType(value) } + + @Input + @Optional + def getOs() { getExten().getOs().getOrNull() } + void setOs(def value) { getExten().os(value) } + + @Input + @Optional + def getType() { getExten().getType().getOrNull() } + void setType(def value) { getExten().type(value) } + + @Input + @Optional + def getMultiArch() { getExten().getMultiArch().getOrNull() } + void setMultiArch(def value) { getExten().multiArch(value) } + + // List properties - return List for backward compatibility + @Input + @Optional + List getPrefixes() { getExten().getPrefixes().getOrElse([]) } + def prefix(String value) { getExten().prefix(value) } + + @Input + @Optional + List getConfigurationFiles() { getExten().getConfigurationFiles().getOrElse([]) } + def configurationFile(String value) { getExten().configurationFile(value) } + + @Input + @Optional + List getPreInstallCommands() { getExten().getPreInstallCommands().getOrElse([]) } + def preInstall(String value) { getExten().preInstall(value) } + def preInstall(File value) { getExten().preInstall(value) } + def preInstallFile(File value) { getExten().preInstallFile(value) } + + @Input + @Optional + List getPostInstallCommands() { getExten().getPostInstallCommands().getOrElse([]) } + def postInstall(String value) { getExten().postInstall(value) } + def postInstall(File value) { getExten().postInstall(value) } + def postInstallFile(File value) { getExten().postInstallFile(value) } + + @Input + @Optional + List getPreUninstallCommands() { getExten().getPreUninstallCommands().getOrElse([]) } + def preUninstall(String value) { getExten().preUninstall(value) } + def preUninstall(File value) { getExten().preUninstall(value) } + def preUninstallFile(File value) { getExten().preUninstallFile(value) } + + @Input + @Optional + List getPostUninstallCommands() { getExten().getPostUninstallCommands().getOrElse([]) } + def postUninstall(String value) { getExten().postUninstall(value) } + def postUninstall(File value) { getExten().postUninstall(value) } + def postUninstallFile(File value) { getExten().postUninstallFile(value) } + + @Input + @Optional + List getTriggerInstallCommands() { getExten().getTriggerInstallCommands().getOrElse([]) } + def triggerInstall(File script, String packageName, String version='', int flag=0) { + getExten().triggerInstall(script, packageName, version, flag) + } + def triggerInstallFile(File value) { getExten().triggerInstallFile(value) } + + @Input + @Optional + List getTriggerUninstallCommands() { getExten().getTriggerUninstallCommands().getOrElse([]) } + def triggerUninstall(File script, String packageName, String version='', int flag=0) { + getExten().triggerUninstall(script, packageName, version, flag) + } + def triggerUninstallFile(File value) { getExten().triggerUninstallFile(value) } + + @Input + @Optional + List getTriggerPostUninstallCommands() { getExten().getTriggerPostUninstallCommands().getOrElse([]) } + def triggerPostUninstall(File script, String packageName, String version='', int flag=0) { + getExten().triggerPostUninstall(script, packageName, version, flag) + } + def triggerPostUninstallFile(File value) { getExten().triggerPostUninstallFile(value) } + + @Input + @Optional + List getPreTransCommands() { getExten().getPreTransCommands().getOrElse([]) } + def preTrans(String value) { getExten().preTrans(value) } + def preTrans(File value) { getExten().preTrans(value) } + + @Input + @Optional + List getPostTransCommands() { getExten().getPostTransCommands().getOrElse([]) } + def postTrans(String value) { getExten().postTrans(value) } + def postTrans(File value) { getExten().postTrans(value) } + + @Input + @Optional + List getCommonCommands() { getExten().getCommonCommands().getOrElse([]) } + def installUtils(String value) { getExten().installUtils(value) } + def installUtils(File value) { getExten().installUtils(value) } + + @Input + @Optional + List getSupplementaryControlFiles() { getExten().getSupplementaryControlFiles().getOrElse([]) } + def supplementaryControl(Object value) { getExten().supplementaryControl(value) } + + @Input + @Optional + List getLinks() { getExten().getLinks().getOrElse([]) } + def link(String path, String target) { getExten().link(path, target) } + def link(String path, String target, int permissions) { getExten().link(path, target, permissions) } + def link(String path, String target, String user, String permissionGroup) { + getExten().link(path, target, user, permissionGroup) + } + def link(String path, String target, int permissions, String user, String permissionGroup) { + getExten().link(path, target, permissions, user, permissionGroup) + } + + @Input + @Optional + List getDependencies() { getExten().getDependencies().getOrElse([]) } + def requires(String packageName) { getExten().requires(packageName) } + def requires(String packageName, String version) { getExten().requires(packageName, version) } + def requires(String packageName, String version, int flag) { getExten().requires(packageName, version, flag) } + + @Input + @Optional + List getObsoletes() { getExten().getObsoletes().getOrElse([]) } + def obsoletes(String packageName) { getExten().obsoletes(packageName) } + def obsoletes(String packageName, String version, int flag) { getExten().obsoletes(packageName, version, flag) } + + @Input + @Optional + List getConflicts() { getExten().getConflicts().getOrElse([]) } + def conflicts(String packageName) { getExten().conflicts(packageName) } + def conflicts(String packageName, String version, int flag) { getExten().conflicts(packageName, version, flag) } + + @Input + @Optional + List getRecommends() { getExten().getRecommends().getOrElse([]) } + def recommends(String packageName) { getExten().recommends(packageName) } + def recommends(String packageName, String version, int flag) { getExten().recommends(packageName, version, flag) } + + @Input + @Optional + List getSuggests() { getExten().getSuggests().getOrElse([]) } + def suggests(String packageName) { getExten().suggests(packageName) } + def suggests(String packageName, String version, int flag) { getExten().suggests(packageName, version, flag) } + + @Input + @Optional + List getEnhances() { getExten().getEnhances().getOrElse([]) } + def enhances(String packageName) { getExten().enhances(packageName) } + def enhances(String packageName, String version, int flag) { getExten().enhances(packageName, version, flag) } + + @Input + @Optional + List getPreDepends() { getExten().getPreDepends().getOrElse([]) } + def preDepends(String packageName) { getExten().preDepends(packageName) } + def preDepends(String packageName, String version, int flag) { getExten().preDepends(packageName, version, flag) } + + @Input + @Optional + List getBreaks() { getExten().getBreaks().getOrElse([]) } + def breaks(String packageName) { getExten().breaks(packageName) } + def breaks(String packageName, String version, int flag) { getExten().breaks(packageName, version, flag) } + + @Input + @Optional + List getReplaces() { getExten().getReplaces().getOrElse([]) } + def replaces(String packageName) { getExten().replaces(packageName) } + def replaces(String packageName, String version, int flag) { getExten().replaces(packageName, version, flag) } + + @Input + @Optional + List getProvides() { getExten().getProvides().getOrElse([]) } + def provides(String packageName) { getExten().provides(packageName) } + def provides(String packageName, String version) { getExten().provides(packageName, version) } + def provides(String packageName, String version, int flag) { getExten().provides(packageName, version, flag) } + + @Input + @Optional + List getDirectories() { getExten().getDirectories().getOrElse([]) } + def directory(String path) { getExten().directory(path) } + def directory(String path, boolean addParents) { getExten().directory(path, addParents) } + def directory(String path, int permissions) { getExten().directory(path, permissions) } + def directory(String path, int permissions, boolean addParents) { getExten().directory(path, permissions, addParents) } + def directory(String path, int permissions, String user, String permissionGroup) { + getExten().directory(path, permissions, user, permissionGroup) + } + def directory(String path, int permissions, String user, String permissionGroup, boolean addParents) { + getExten().directory(path, permissions, user, permissionGroup, addParents) + } + + @Input + @Optional + Map getCustomFields() { getExten().getCustomFields().getOrElse([:]) } + def customField(String key, String val) { getExten().customField(key, val) } + def customField(Map fields) { getExten().customField(fields) } + + // Apply conventions to task extension properties using modern Property API + protected void applyConventions(Project project = project) { + // Apply default conventions FIRST (lowest priority) + exten.packageName.convention(getArchiveBaseName()) + exten.release.convention(getArchiveClassifier()) + exten.version.convention(getProviders().provider { + sanitizeVersion(parentExten?.getVersion()?.getOrNull() ?: project.getVersion().toString()) + }) + exten.epoch.convention(0) + exten.signingKeyId.convention('') + exten.signingKeyPassphrase.convention('') + exten.signingKeyRingFile.convention( + projectLayout.file(getProviders().provider { + File defaultFile = new File(System.getProperty('user.home'), '.gnupg/secring.gpg') + defaultFile.exists() ? defaultFile : null }) - mapping.map('license', { parentExten?.getLicense() ?: '' }) - mapping.map('packager', { parentExten?.getPackager() ?: System.getProperty('user.name', '') }) - mapping.map('distribution', { parentExten?.getDistribution() ?: '' }) - mapping.map('vendor', { parentExten?.getVendor() ?: '' }) - mapping.map('url', { parentExten?.getUrl() ?: '' }) - mapping.map('sourcePackage', { parentExten?.getSourcePackage() ?: '' }) - mapping.map('createDirectoryEntry', { parentExten?.getCreateDirectoryEntry() ?: false }) - mapping.map('priority', { parentExten?.getPriority() ?: 'optional' }) - - mapping.map('preInstallFile', { parentExten?.getPreInstallFile() }) - mapping.map('postInstallFile', { parentExten?.getPostInstallFile() }) - mapping.map('preUninstallFile', { parentExten?.getPreUninstallFile() }) - mapping.map('postUninstallFile', { parentExten?.getPostUninstallFile() }) - - // Task Specific - if(GradleVersion.current().compareTo(GradleVersion.version("7.0.0")) >= 0) { - getArchiveFileName().convention(project.provider(new Callable() { - @Override - String call() throws Exception { - return assembleArchiveName() - } - })) - getArchiveVersion().convention(determineArchiveVersion()) - } else { - mapping.map('archiveFile', { determineArchiveFile() }) - mapping.map('archiveName', { assembleArchiveName() }) - mapping.map('archivePath', { determineArchivePath() }) - mapping.map('archiveVersion', { determineArchiveVersion() }) - } + ) + exten.user.convention(getProviders().provider { getPackager() }) + exten.maintainer.convention(getProviders().provider { getPackager() }) + exten.uploaders.convention(getProviders().provider { getPackager() }) + exten.permissionGroup.convention('') + exten.setgid.convention(false) + exten.buildHost.convention(HOST_NAME) + exten.summary.convention(exten.packageName) + exten.packageDescription.convention(getProviders().provider { project.getDescription() ?: '' }) + exten.license.convention('') + exten.packager.convention(System.getProperty('user.name', '')) + exten.distribution.convention('') + exten.vendor.convention('') + exten.url.convention('') + exten.sourcePackage.convention('') + exten.createDirectoryEntry.convention(false) + exten.priority.convention('optional') + + // Then apply conventions from parentExten (higher priority - override defaults) + // Only override if parentExten has a value + if (parentExten) { + if (parentExten.packageName.isPresent()) exten.packageName.convention(parentExten.packageName) + if (parentExten.release.isPresent()) exten.release.convention(parentExten.release) + if (parentExten.version.isPresent()) exten.version.convention(parentExten.version) + if (parentExten.epoch.isPresent()) exten.epoch.convention(parentExten.epoch) + if (parentExten.signingKeyId.isPresent()) exten.signingKeyId.convention(parentExten.signingKeyId) + if (parentExten.signingKeyPassphrase.isPresent()) exten.signingKeyPassphrase.convention(parentExten.signingKeyPassphrase) + if (parentExten.signingKeyRingFile.isPresent()) exten.signingKeyRingFile.convention(parentExten.signingKeyRingFile) + if (parentExten.user.isPresent()) exten.user.convention(parentExten.user) + if (parentExten.maintainer.isPresent()) exten.maintainer.convention(parentExten.maintainer) + if (parentExten.uploaders.isPresent()) exten.uploaders.convention(parentExten.uploaders) + if (parentExten.permissionGroup.isPresent()) exten.permissionGroup.convention(parentExten.permissionGroup) + if (parentExten.setgid.isPresent()) exten.setgid.convention(parentExten.setgid) + if (parentExten.packageGroup.isPresent()) exten.packageGroup.convention(parentExten.packageGroup) + if (parentExten.buildHost.isPresent()) exten.buildHost.convention(parentExten.buildHost) + if (parentExten.summary.isPresent()) exten.summary.convention(parentExten.summary) + if (parentExten.packageDescription.isPresent()) exten.packageDescription.convention(parentExten.packageDescription) + if (parentExten.license.isPresent()) exten.license.convention(parentExten.license) + if (parentExten.packager.isPresent()) exten.packager.convention(parentExten.packager) + if (parentExten.distribution.isPresent()) exten.distribution.convention(parentExten.distribution) + if (parentExten.vendor.isPresent()) exten.vendor.convention(parentExten.vendor) + if (parentExten.url.isPresent()) exten.url.convention(parentExten.url) + if (parentExten.sourcePackage.isPresent()) exten.sourcePackage.convention(parentExten.sourcePackage) + if (parentExten.createDirectoryEntry.isPresent()) exten.createDirectoryEntry.convention(parentExten.createDirectoryEntry) + if (parentExten.priority.isPresent()) exten.priority.convention(parentExten.priority) + if (parentExten.preInstallFile.isPresent()) exten.preInstallFile.convention(parentExten.preInstallFile) + if (parentExten.postInstallFile.isPresent()) exten.postInstallFile.convention(parentExten.postInstallFile) + if (parentExten.preUninstallFile.isPresent()) exten.preUninstallFile.convention(parentExten.preUninstallFile) + if (parentExten.postUninstallFile.isPresent()) exten.postUninstallFile.convention(parentExten.postUninstallFile) } - } - private String sanitizeVersion() { - sanitizeVersion(parentExten?.getVersion() ?: project.getVersion().toString()) + // Task-specific conventions + if(GradleVersion.current().compareTo(GradleVersion.version("7.0.0")) >= 0) { + getArchiveFileName().convention(getProviders().provider { assembleArchiveName() }) + getArchiveVersion().convention(determineArchiveVersion()) + } } private String sanitizeVersion(String version) { @@ -159,14 +574,15 @@ abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { abstract String assembleArchiveName() Provider determineArchiveFile() { - Property regularFile = objectFactory.fileProperty() - regularFile.set(new DestinationFile(new File(getDestinationDirectory().get().asFile.path, assembleArchiveName()))) - return regularFile + // Use map() to avoid eager .get() call - lazily compute the file when needed + return getDestinationDirectory().map { directory -> + new DestinationFile(new File(directory.asFile.path, assembleArchiveName())) as RegularFile + } } Provider determineArchiveVersion() { - String version = sanitizeVersion(parentExten?.getVersion() ?: project.getVersion().toString()) - Property archiveVersion = objectFactory.property(String) + String version = sanitizeVersion(parentExten?.getVersion()?.getOrNull() ?: project.getVersion().toString()) + Property archiveVersion = getObjectFactory().property(String) archiveVersion.set(version) return archiveVersion } @@ -198,147 +614,119 @@ abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { @Input @Optional - List getAllConfigurationFiles() { - return getConfigurationFiles() + (parentExten?.getConfigurationFiles() ?: []) + List getAllConfigurationFiles() { + return getConfigurationFiles() + (parentExten?.getConfigurationFiles()?.getOrElse([]) ?: []) } @Input @Optional - List getAllPreInstallCommands() { - return getPreInstallCommands() + (parentExten?.getPreInstallCommands() ?: []) + List getAllPreInstallCommands() { + return getPreInstallCommands() + (parentExten?.getPreInstallCommands()?.getOrElse([]) ?: []) } @Input @Optional - List getAllPostInstallCommands() { - return getPostInstallCommands() + (parentExten?.getPostInstallCommands() ?: []) + List getAllPostInstallCommands() { + return getPostInstallCommands() + (parentExten?.getPostInstallCommands()?.getOrElse([]) ?: []) } @Input @Optional - List getAllPreUninstallCommands() { - return getPreUninstallCommands() + (parentExten?.getPreUninstallCommands() ?: []) + List getAllPreUninstallCommands() { + return getPreUninstallCommands() + (parentExten?.getPreUninstallCommands()?.getOrElse([]) ?: []) } @Input @Optional - List getAllPostUninstallCommands() { - return getPostUninstallCommands() + (parentExten?.getPostUninstallCommands() ?: []) + List getAllPostUninstallCommands() { + return getPostUninstallCommands() + (parentExten?.getPostUninstallCommands()?.getOrElse([]) ?: []) } @Input @Optional List getAllTriggerIn() { - return getTriggerInstallCommands() + (parentExten?.getTriggerInstallCommands() ?: []) + return getTriggerInstallCommands() + (parentExten?.getTriggerInstallCommands()?.getOrElse([]) ?: []) } @Input @Optional List getAllTriggerUn() { - return getTriggerUninstallCommands() + (parentExten?.getTriggerUninstallCommands() ?: []) + return getTriggerUninstallCommands() + (parentExten?.getTriggerUninstallCommands()?.getOrElse([]) ?: []) } @Input @Optional List getAllTriggerPostUn() { - return getTriggerPostUninstallCommands() + (parentExten?.getTriggerPostUninstallCommands() ?: []) + return getTriggerPostUninstallCommands() + (parentExten?.getTriggerPostUninstallCommands()?.getOrElse([]) ?: []) } @Input @Optional - List getAllPreTransCommands() { - return getPreTransCommands() + (parentExten?.getPreTransCommands() ?: []) + List getAllPreTransCommands() { + return getPreTransCommands() + (parentExten?.getPreTransCommands()?.getOrElse([]) ?: []) } @Input @Optional - List getAllPostTransCommands() { - return getPostTransCommands() + (parentExten?.getPostTransCommands() ?: []) + List getAllPostTransCommands() { + return getPostTransCommands() + (parentExten?.getPostTransCommands()?.getOrElse([]) ?: []) } @Input @Optional - List getAllCommonCommands() { - return getCommonCommands() + (parentExten?.getCommonCommands() ?: []) + List getAllCommonCommands() { + return getCommonCommands() + (parentExten?.getCommonCommands()?.getOrElse([]) ?: []) } /** - * @return supplementary control files consisting of a combination of Strings and Files + * @return supplementary control files consisting of Strings (file paths) */ @Input @Optional - List getAllSupplementaryControlFiles() { - return getSupplementaryControlFiles() + (parentExten?.getSupplementaryControlFiles() ?: []) + List getAllSupplementaryControlFiles() { + return getSupplementaryControlFiles() + (parentExten?.getSupplementaryControlFiles()?.getOrElse([]) ?: []) } @Input @Optional List getAllLinks() { - if (parentExten) { - return getLinks() + parentExten.getLinks() - } else { - return getLinks() - } + return getLinks() + (parentExten?.getLinks()?.getOrElse([]) ?: []) } @Input @Optional List getAllDependencies() { - if (parentExten) { - return getDependencies() + parentExten.getDependencies() - } else { - return getDependencies() - } + return getDependencies() + (parentExten?.getDependencies()?.getOrElse([]) ?: []) } @Input @Optional def getAllPrefixes() { - if (parentExten) { - return (getPrefixes() + parentExten.getPrefixes()).unique() - } else { - return getPrefixes() - } + return (getPrefixes() + (parentExten?.getPrefixes()?.getOrElse([]) ?: [])).unique() } @Input @Optional List getAllProvides() { - if (parentExten) { - return parentExten.getProvides() + getProvides() - } else { - return getProvides() - } + return getProvides() + (parentExten?.getProvides()?.getOrElse([]) ?: []) } @Input @Optional List getAllObsoletes() { - if (parentExten) { - return getObsoletes() + parentExten.getObsoletes() - } else { - return getObsoletes() - } + return getObsoletes() + (parentExten?.getObsoletes()?.getOrElse([]) ?: []) } @Input @Optional List getAllConflicts() { - if (parentExten) { - return getConflicts() + parentExten.getConflicts() - } else { - return getConflicts() - } + return getConflicts() + (parentExten?.getConflicts()?.getOrElse([]) ?: []) } @Input @Optional List getAllDirectories() { - if (parentExten) { - return getDirectories() + parentExten.getDirectories() - } else { - return getDirectories() - } + return getDirectories() + (parentExten?.getDirectories()?.getOrElse([]) ?: []) } @Override @@ -388,7 +776,7 @@ abstract class SystemPackagingTask extends OsPackageAbstractArchiveTask { @PathSensitive(PathSensitivity.RELATIVE) @SkipWhenEmpty FileCollection getFakeFiles() { - project.files('fake') + getObjectFactory().fileCollection().from('fake') } @Override diff --git a/src/main/groovy/com/netflix/gradle/plugins/rpm/Rpm.groovy b/src/main/groovy/com/netflix/gradle/plugins/rpm/Rpm.groovy index 3066950c..d397e6e0 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/rpm/Rpm.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/rpm/Rpm.groovy @@ -20,7 +20,10 @@ import com.netflix.gradle.plugins.packaging.AbstractPackagingCopyAction import com.netflix.gradle.plugins.packaging.SystemPackagingTask import com.netflix.gradle.plugins.utils.DeprecationLoggerUtils import groovy.transform.CompileDynamic +import org.gradle.api.Project import org.gradle.api.file.ProjectLayout +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Optional import org.gradle.api.tasks.PathSensitive @@ -29,8 +32,6 @@ import org.gradle.work.DisableCachingByDefault import org.redline_rpm.header.Architecture import org.redline_rpm.header.Os import org.redline_rpm.header.RpmType -import org.gradle.api.internal.ConventionMapping -import org.gradle.api.internal.IConventionAware import javax.inject.Inject @@ -46,7 +47,6 @@ abstract class Rpm extends SystemPackagingTask { Rpm(ProjectLayout projectLayout) { super(projectLayout) archiveExtension.set 'rpm' - notCompatibleWithConfigurationCache("nebula.ospackage does not support configuration cache") } @Override @@ -56,7 +56,8 @@ abstract class Rpm extends SystemPackagingTask { name += getVersion() ? "-${getVersion()}" : '' name += getRelease() ? "-${getRelease()}" : '' name += getArchString() ? ".${getArchString()}" : '' - name += getArchiveExtension().getOrNull() ? ".${getArchiveExtension().getOrNull()}" : '' + def ext = getArchiveExtension().getOrNull() + name += ext ? ".${ext}" : '' } return name; @@ -68,27 +69,38 @@ abstract class Rpm extends SystemPackagingTask { } @Override - protected void applyConventions() { - super.applyConventions() - - // For all mappings, we're only being called if it wasn't explicitly set on the task. In which case, we'll want - // to pull from the parentExten. And only then would we fallback on some other value. - ConventionMapping mapping = ((IConventionAware) this).getConventionMapping() - - // Could come from extension - mapping.map('fileType', { parentExten?.getFileType() }) - mapping.map('addParentDirs', { - // beware the Elvis operator in Groovy ... - parentExten?.getAddParentDirs() != null ? parentExten?.getAddParentDirs() : true - }) - mapping.map('archStr', { - parentExten?.getArchStr()?:Architecture.NOARCH.name() - }) - mapping.map('os', { parentExten?.getOs()?:Os.UNKNOWN}) - mapping.map('type', { parentExten?.getType()?:RpmType.BINARY }) - - // NOTE: Believe parentExten is always null - mapping.map('prefixes', { parentExten?.getPrefixes()?:[] }) + protected void applyConventions(Project project) { + super.applyConventions(project) + + // Apply default conventions FIRST (lowest priority) + exten.addParentDirs.convention(true) + exten.archStr.convention(Architecture.NOARCH.name()) + exten.os.convention(Os.UNKNOWN) + exten.type.convention(RpmType.BINARY) + exten.prefixes.convention([]) + + // Then apply conventions from parentExten (higher priority - override defaults) + // Only override if parentExten has a value + if (parentExten) { + if (parentExten.fileType.isPresent()) { + exten.fileType.convention(parentExten.fileType) + } + if (parentExten.addParentDirs.isPresent()) { + exten.addParentDirs.convention(parentExten.addParentDirs) + } + if (parentExten.archStr.isPresent()) { + exten.archStr.convention(parentExten.archStr) + } + if (parentExten.os.isPresent()) { + exten.os.convention(parentExten.os) + } + if (parentExten.type.isPresent()) { + exten.type.convention(parentExten.type) + } + if (parentExten.prefixes.isPresent()) { + exten.prefixes.convention(parentExten.prefixes) + } + } } void prefixes(String... addPrefixes) { @@ -96,7 +108,7 @@ abstract class Rpm extends SystemPackagingTask { } List getPrefixes() { - exten.prefixes + exten.prefixes.getOrElse([]) } public File getChangeLogFile() { diff --git a/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmCopyAction.groovy b/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmCopyAction.groovy index f9add59c..a4d9c5a7 100755 --- a/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmCopyAction.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmCopyAction.groovy @@ -20,6 +20,7 @@ import com.netflix.gradle.plugins.packaging.AbstractPackagingCopyAction import com.netflix.gradle.plugins.packaging.Dependency import com.netflix.gradle.plugins.packaging.Directory import com.netflix.gradle.plugins.packaging.Link +import com.netflix.gradle.plugins.packaging.Trigger import com.netflix.gradle.plugins.rpm.validation.RpmTaskPropertiesValidator import com.netflix.gradle.plugins.utils.DeprecationLoggerUtils import com.netflix.gradle.plugins.utils.FilePermissionUtil @@ -41,6 +42,7 @@ import static com.netflix.gradle.plugins.utils.GradleUtils.lookup @CompileDynamic class RpmCopyAction extends AbstractPackagingCopyAction { static final Logger logger = LoggerFactory.getLogger(RpmCopyAction.class) + private static final int SETGID_BIT = 02000 // Unix setgid permission bit Builder builder boolean includeStandardDefines = true // candidate for being pushed up to packaging level @@ -90,55 +92,34 @@ class RpmCopyAction extends AbstractPackagingCopyAction { String sourcePackage = task.sourcePackage if (!sourcePackage) { // need a source package because createrepo will assume your package is a source package without it - sourcePackage = builder.defaultSourcePackage + sourcePackage = task.packageName + "-src.rpm" } builder.addHeaderEntry HeaderTag.SOURCERPM, sourcePackage - if (!task.allPreInstallCommands?.empty) { + if (task.allPreInstallCommands) { builder.setPreInstallScript(scriptWithUtils(task.allCommonCommands, task.allPreInstallCommands)) } - if (!task.allPostInstallCommands?.empty) { + if (task.allPostInstallCommands) { builder.setPostInstallScript(scriptWithUtils(task.allCommonCommands, task.allPostInstallCommands)) } - if (!task.allPreUninstallCommands?.empty) { + if (task.allPreUninstallCommands) { builder.setPreUninstallScript(scriptWithUtils(task.allCommonCommands, task.allPreUninstallCommands)) } - if (!task.allPostUninstallCommands?.empty) { + if (task.allPostUninstallCommands) { builder.setPostUninstallScript(scriptWithUtils(task.allCommonCommands, task.allPostUninstallCommands)) } - if (!task.allTriggerIn?.empty) { - task.allTriggerIn.each { trigger -> - def dependencyMap = [:] - dependencyMap.putAt(trigger.dependency.packageName, - new IntString(trigger.dependency.flag, trigger.dependency.version)) - builder.addTrigger(trigger.command, null, dependencyMap, Flags.SCRIPT_TRIGGERIN) - } - } - if (!task.allTriggerUn?.empty) { - task.allTriggerUn.each { trigger -> - def dependencyMap = [:] - dependencyMap.putAt(trigger.dependency.packageName, - new IntString(trigger.dependency.flag, trigger.dependency.version)) - builder.addTrigger(trigger.command, null, dependencyMap, Flags.SCRIPT_TRIGGERUN) - } - } - if (!task.allTriggerPostUn?.empty) { - task.allTriggerPostUn.each { trigger -> - def dependencyMap = [:] - dependencyMap.putAt(trigger.dependency.packageName, - new IntString(trigger.dependency.flag, trigger.dependency.version)) - builder.addTrigger(trigger.command, null, dependencyMap, Flags.SCRIPT_TRIGGERPOSTUN) - } - } + addTriggers(task.allTriggerIn, Flags.SCRIPT_TRIGGERIN) + addTriggers(task.allTriggerUn, Flags.SCRIPT_TRIGGERUN) + addTriggers(task.allTriggerPostUn, Flags.SCRIPT_TRIGGERPOSTUN) - if (!task.allPreTransCommands?.empty) { + if (task.allPreTransCommands) { // pretrans* scriptlets are special. They may be run in an // environment where no shell exists. It's recommended that they // be avoided where possible, but where not, written in Lua: // https://fedoraproject.org/wiki/Packaging:Scriptlets#The_.25pretrans_Scriptlet builder.setPreTransScript(concat(task.allPreTransCommands)) } - if (!task.allPostTransCommands?.empty) { + if (task.allPostTransCommands) { builder.setPostTransScript(scriptWithUtils(task.allCommonCommands, task.allPostTransCommands)) } @@ -231,7 +212,7 @@ class RpmCopyAction extends AbstractPackagingCopyAction { setgid = task.setgid } if (setgid) { - dirMode = dirMode | 02000 + dirMode = dirMode | SETGID_BIT } rpmFileVisitorStrategy.addDirectory(dirDetails, dirMode, directive, user, group, addParentsDir) } @@ -266,8 +247,8 @@ class RpmCopyAction extends AbstractPackagingCopyAction { @Override protected void addDirectory(Directory directory) { - def user = directory.user ? directory.user : task.user - def permissionGroup = directory.permissionGroup ? directory.permissionGroup : task.permissionGroup + def user = directory.user ?: task.user + def permissionGroup = directory.permissionGroup ?: task.permissionGroup builder.addDirectory(directory.path, directory.permissions, null, user, permissionGroup, directory.addParents) } @@ -285,6 +266,14 @@ class RpmCopyAction extends AbstractPackagingCopyAction { return new Builder() } + private void addTriggers(List triggers, int flagType) { + triggers?.each { trigger -> + def dependencyMap = [(trigger.dependency.packageName): + new IntString(trigger.dependency.flag, trigger.dependency.version)] + builder.addTrigger(trigger.command, null, dependencyMap, flagType) + } + } + String standardScriptDefines() { String result DeprecationLoggerUtils.whileDisabled { @@ -299,7 +288,7 @@ class RpmCopyAction extends AbstractPackagingCopyAction { return result } - String scriptWithUtils(List utils, List scripts) { + String scriptWithUtils(List utils, List scripts) { def l = [] def stdDefines = standardScriptDefines() if (stdDefines) { diff --git a/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmPlugin.groovy b/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmPlugin.groovy index 23776f95..1c7552b9 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmPlugin.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/rpm/RpmPlugin.groovy @@ -36,22 +36,11 @@ class RpmPlugin implements Plugin { project.ext.Rpm = Rpm.class - Builder.metaClass.getDefaultSourcePackage() { - format.getLead().getName() + "-src.rpm" - } - - Directive.metaClass.or = { Directive other -> - new Directive(delegate.flag | other.flag) - } - // Some defaults, if not set by the user - project.tasks.withType(Rpm).configureEach(new Action() { - @Override - void execute(Rpm rpm) { - RpmPlugin.applyAliases(rpm) // RPM Specific aliases - rpm.applyConventions() - } - }) + project.tasks.withType(Rpm).configureEach { Rpm rpm -> + RpmPlugin.applyAliases(rpm) // RPM Specific aliases + rpm.applyConventions(project) + } } def static applyAliases(def dynamicObjectAware) { diff --git a/src/main/groovy/com/netflix/gradle/plugins/utils/FilePermissionUtil.groovy b/src/main/groovy/com/netflix/gradle/plugins/utils/FilePermissionUtil.groovy index 9cf808ea..6d298024 100644 --- a/src/main/groovy/com/netflix/gradle/plugins/utils/FilePermissionUtil.groovy +++ b/src/main/groovy/com/netflix/gradle/plugins/utils/FilePermissionUtil.groovy @@ -14,6 +14,11 @@ import org.gradle.api.file.SyncSpec @CompileDynamic class FilePermissionUtil { + private static final int DEFAULT_FILE_PERMISSION = 0644 // rw-r--r-- (420 in decimal) + private static final int EXECUTE_MASK = 0111 // Executable bits for owner, group, and others + private static final int OWNER_RWX_GROUP_RX_OTHER_RX = 0755 // rwxr-xr-x + private static final int ALL_READ_EXECUTE = 0555 // r-xr-xr-x + /** * Get the unix permission of a file. * @@ -37,18 +42,18 @@ class FilePermissionUtil { int newApiMode = details.permissions.toUnixNumeric() try { - if (details.file?.canExecute() && (newApiMode & 0111) == 0) { + if (details.file?.canExecute() && (newApiMode & EXECUTE_MASK) == 0) { // File is executable but new API didn't detect it - use filesystem check boolean readable = details.file.canRead() boolean writable = details.file.canWrite() boolean executable = details.file.canExecute() if (readable && writable && executable) { - return 0755 // rwxr-xr-x + return OWNER_RWX_GROUP_RX_OTHER_RX } else if (readable && executable) { - return 0555 // r-xr-xr-x + return ALL_READ_EXECUTE } else { - return 0644 // rw-r--r-- + return DEFAULT_FILE_PERMISSION } } } catch (Exception e) { @@ -91,10 +96,10 @@ class FilePermissionUtil { def hasExplicitConfiguration = false try { // Look for signs of explicit configuration - if (copySpecInternal.hasProperty('includes') && copySpecInternal.includes?.size() > 0) { + if (copySpecInternal.hasProperty('includes') && copySpecInternal.includes) { hasExplicitConfiguration = true } - if (copySpecInternal.hasProperty('excludes') && copySpecInternal.excludes?.size() > 0) { + if (copySpecInternal.hasProperty('excludes') && copySpecInternal.excludes) { hasExplicitConfiguration = true } } catch (Exception e) { @@ -102,7 +107,7 @@ class FilePermissionUtil { } // If we have explicit configuration OR the permission is not default 644, treat as explicit - if (hasExplicitConfiguration || numeric != 420) { + if (hasExplicitConfiguration || numeric != DEFAULT_FILE_PERMISSION) { return numeric } else { // Default 644 with no explicit configuration - treat as default diff --git a/src/test/groovy/com/netflix/gradle/plugins/ConfigurationCacheSpec.groovy b/src/test/groovy/com/netflix/gradle/plugins/ConfigurationCacheSpec.groovy new file mode 100644 index 00000000..b5615c63 --- /dev/null +++ b/src/test/groovy/com/netflix/gradle/plugins/ConfigurationCacheSpec.groovy @@ -0,0 +1,1042 @@ +/* + * Copyright 2014-2019 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.gradle.plugins + +import spock.lang.Ignore + +/** + * Tests that verify configuration cache support for Debian packaging. + * These tests run tasks twice to ensure the configuration cache is stored and reused. + */ +class ConfigurationCacheSpec extends BaseIntegrationTestKitSpec { + + def 'deb plugin works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'test-package' + version = '1.0.0' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + // Create a dummy file to package + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'test.txt').text = 'test content' + + when: 'run myDeb first time - stores configuration cache' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time - reuses configuration cache' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'build artifact exists' + new File(projectDir, "build/distributions/test-package_1.0.0_all.deb").exists() + } + + def 'daemon plugin works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage-daemon' + id 'com.netflix.nebula.deb' + } + + daemon { + daemonName = 'test-daemon' + command = 'sleep infinity' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'daemon-test' + version = '1.0.0' + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/daemon-test_1.0.0_all.deb').exists() + } + + def 'application plugin works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'application' + id 'com.netflix.nebula.ospackage-application' + } + + application { + mainClass = 'com.test.Main' + } + + ospackage_application { + prefix = '/opt' + } + """.stripIndent() + + // Create a minimal Main class + def srcDir = new File(projectDir, 'src/main/java/com/test') + srcDir.mkdirs() + new File(srcDir, 'Main.java').text = """ + package com.test; + public class Main { + public static void main(String[] args) { + System.out.println("Hello"); + } + } + """.stripIndent() + + when: 'run buildDeb first time' + def result1 = runTasks('buildDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run buildDeb second time' + def result2 = runTasks('buildDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + } + + def 'multiple daemons work with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage-daemon' + id 'com.netflix.nebula.deb' + } + + daemons { + daemon { + daemonName = 'daemon1' + command = 'sleep infinity' + } + daemon { + daemonName = 'daemon2' + command = 'exit 0' + user = 'nobody' + } + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'multi-daemon' + version = '1.0.0' + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/multi-daemon_1.0.0_all.deb').exists() + } + + def 'custom daemon templates work with configuration cache'() { + given: + // Create custom template directory + def templatesDir = new File(projectDir, 'templates') + templatesDir.mkdirs() + new File(templatesDir, 'initd.tpl').text = '''#!/bin/sh +# Custom init script for ${daemonName} +'''.stripIndent() + new File(templatesDir, 'run.tpl').text = '''#!/bin/sh +exec ${command} +'''.stripIndent() + new File(templatesDir, 'log-run.tpl').text = '''#!/bin/sh +exec multilog t ./main +'''.stripIndent() + new File(templatesDir, 'postInstall.tpl').text = '' + + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage-daemon' + id 'com.netflix.nebula.deb' + } + + daemonsTemplates { + folder = 'templates' + } + + daemon { + daemonName = 'custom-daemon' + command = 'sleep infinity' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'custom-templates' + version = '1.0.0' + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/custom-templates_1.0.0_all.deb').exists() + } + + def 'ospackage-base plugin works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage-base' + } + + tasks.register('customDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'custom-package' + version = '2.0.0' + + from('src') { + into '/opt/custom' + } + } + """.stripIndent() + + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'custom.txt').text = 'custom content' + + when: 'run customDeb first time' + def result1 = runTasks('customDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run customDeb second time' + def result2 = runTasks('customDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/custom-package_2.0.0_all.deb').exists() + } + + def 'complex deb configuration works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('complexDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'complex-package' + version = '3.0.0' + + // Multiple from blocks + from('src1') { + into '/opt/app1' + } + from('src2') { + into '/opt/app2' + } + + // Links + link('/usr/bin/myapp', '/opt/app1/myapp') + + // PreInstall/PostInstall + preInstall 'echo "Installing..."' + postInstall 'echo "Installed!"' + } + """.stripIndent() + + // Create source directories + ['src1', 'src2'].each { dir -> + def srcDir = new File(projectDir, dir) + srcDir.mkdirs() + new File(srcDir, 'file.txt').text = "content in ${dir}" + } + + when: 'run complexDeb first time' + def result1 = runTasks('complexDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run complexDeb second time' + def result2 = runTasks('complexDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, "build/distributions/complex-package_3.0.0_all.deb").exists() + } + + def 'buildDeb task works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage' + } + + ospackage { + packageName = 'simple-test' + version = '1.0' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'application content' + + when: 'run buildDeb first time' + def result1 = runTasks('buildDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run buildDeb second time' + def result2 = runTasks('buildDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, "build/distributions/simple-test_1.0_all.deb").exists() + } + + def 'no Task.getProject() deprecation warnings when using project version description and supplementaryControl'() { + given: + def srcDir = new File(projectDir, 'src/app') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + def controlFile = new File(projectDir, 'src/changelog') + controlFile.text = 'Initial release' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + version = '1.0.0' + description = 'My application' + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'deprecation-test' + supplementaryControl 'src/changelog' + + from('src/app') { + into '/opt/app' + } + } + """.stripIndent() + + when: + def result = runTasks('myDeb', '--warning-mode', 'all') + + then: 'no Task.project at execution time deprecation warning' + !result.output.contains('Task.project at execution time has been deprecated') + !result.output.contains('Invocation of Task.project at execution time') + } + + def 'ospackage parent extension propagates version and user to child tasks with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage' + } + + ospackage { + packageName = 'parent-ext-test' + version = '3.0.0' + user = 'myuser' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run buildDeb first time' + def result1 = runTasks('buildDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run buildDeb second time' + def result2 = runTasks('buildDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created with values from parent extension' + new File(projectDir, 'build/distributions/parent-ext-test_3.0.0_all.deb').exists() + } + + def 'rpm derives version from project.version with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.rpm' + } + + version = '2.0.0' + + tasks.register('myRpm', com.netflix.gradle.plugins.rpm.Rpm) { + packageName = 'rpm-project-version-test' + release = '1' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myRpm first time' + def result1 = runTasks('myRpm', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myRpm second time' + def result2 = runTasks('myRpm', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created with version from project' + new File(projectDir, 'build/distributions/rpm-project-version-test-2.0.0-1.noarch.rpm').exists() + } + + def 'packager convention propagates to user maintainer and uploaders with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'packager-test' + version = '1.0.0' + packager = 'Test Packager' + // user, maintainer, uploaders intentionally not set — should default to packager + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/packager-test_1.0.0_all.deb').exists() + } + + def 'signing key ring file convention works with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'signing-convention-test' + version = '1.0.0' + // Configure signing key ID and passphrase — ring file uses the lazy convention + // that checks ~/.gnupg/secring.gpg at execution time (not config time) + signingKeyId = 'ABCD1234' + signingKeyPassphrase = 'test-passphrase' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created (unsigned since ring file does not exist)' + new File(projectDir, 'build/distributions/signing-convention-test_1.0.0_all.deb').exists() + } + + def 'supplementaryControl files work with configuration cache'() { + given: + def controlFile = new File(projectDir, 'src/changelog') + controlFile.parentFile.mkdirs() + controlFile.text = 'Initial release' + + def srcDir = new File(projectDir, 'src/app') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'supplementary-test' + version = '1.0.0' + supplementaryControl 'src/changelog' + + from('src/app') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/supplementary-test_1.0.0_all.deb').exists() + } + + def 'version and description derived from project work with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + version = '2.5.0' + description = 'My application' + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'project-defaults-test' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created with version from project' + new File(projectDir, 'build/distributions/project-defaults-test_2.5.0_all.deb').exists() + } + + def 'rpm plugin works with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.rpm' + } + + tasks.register('myRpm', com.netflix.gradle.plugins.rpm.Rpm) { + packageName = 'rpm-cc-test' + version = '1.0.0' + release = '1' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myRpm first time' + def result1 = runTasks('myRpm', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myRpm second time' + def result2 = runTasks('myRpm', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/rpm-cc-test-1.0.0-1.noarch.rpm').exists() + } + + def 'configuration cache is invalidated and re-stored when build file changes'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'cache-invalidation-test' + version = '1.0.0' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'first run - CC stored' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: + result1.output.contains('Configuration cache entry stored') + + when: 'build version changes' + buildFile.text = buildFile.text.replace("version = '1.0.0'", "version = '2.0.0'") + + and: 're-run with changed build file' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'CC is re-stored because the build configuration changed' + result2.output.contains('Configuration cache entry stored') + + and: 'new version artifact is built' + new File(projectDir, 'build/distributions/cache-invalidation-test_2.0.0_all.deb').exists() + } + + def 'task re-executes when source files change while configuration cache is reused'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + def srcFile = new File(srcDir, 'app.txt') + srcFile.text = 'original content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'incremental-cc-test' + version = '1.0.0' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'first run - CC stored, task executes' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: + result1.output.contains('Configuration cache entry stored') + + when: 'second run - CC reused, task is UP-TO-DATE' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: + result2.output.contains('Configuration cache entry reused') + result2.output.contains('UP-TO-DATE') + + when: 'source file is modified' + srcFile.text = 'modified content' + + and: 'third run - CC reused but task re-executes' + def result3 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is still reused (build file unchanged)' + result3.output.contains('Configuration cache entry reused') + + and: 'task executed because its inputs changed' + !result3.output.contains('1 actionable task: 1 up-to-date') + } + + def 'rpm plugin produces no Task.getProject() deprecation warnings'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.rpm' + } + + version = '2.0.0' + + tasks.register('myRpm', com.netflix.gradle.plugins.rpm.Rpm) { + packageName = 'rpm-deprecation-test' + release = '1' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: + def result = runTasks('myRpm', '--warning-mode', 'all') + + then: 'no Task.project at execution time deprecation warnings' + !result.output.contains('Task.project at execution time has been deprecated') + !result.output.contains('Invocation of Task.project at execution time') + } + + def 'packager convention produces no Task.getProject() deprecation warnings'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'packager-deprecation-test' + version = '1.0.0' + packager = 'CI System' + // user, maintainer, uploaders all default to packager via lazy providers + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: + def result = runTasks('myDeb', '--warning-mode', 'all') + + then: 'no Task.project at execution time deprecation warnings' + !result.output.contains('Task.project at execution time has been deprecated') + !result.output.contains('Invocation of Task.project at execution time') + } + + def 'deb packaged from configuration dependency works with configuration cache'() { + given: + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + id 'java' + } + + repositories { + mavenCentral() + } + + configurations { + bundled + } + + dependencies { + bundled 'log4j:log4j:1.2.17' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'config-dep-test' + version = '1.0.0' + + from(configurations.bundled) { + into '/opt/libs' + createDirectoryEntry = true + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/config-dep-test_1.0.0_all.deb').exists() + } + + def 'deb with file-based install scripts works with configuration cache'() { + given: + def scriptsDir = new File(projectDir, 'scripts') + scriptsDir.mkdirs() + new File(scriptsDir, 'pre.sh').text = '#!/bin/sh\necho "pre-install"' + new File(scriptsDir, 'post.sh').text = '#!/bin/sh\necho "post-install"' + new File(scriptsDir, 'pre-rm.sh').text = '#!/bin/sh\necho "pre-remove"' + new File(scriptsDir, 'post-rm.sh').text = '#!/bin/sh\necho "post-remove"' + + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'scripts-test' + version = '1.0.0' + + preInstall file('scripts/pre.sh') + postInstall file('scripts/post.sh') + preUninstall file('scripts/pre-rm.sh') + postUninstall file('scripts/post-rm.sh') + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/scripts-test_1.0.0_all.deb').exists() + } + + def 'buildDeb and buildRpm both work with configuration cache in the same build'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.txt').text = 'app content' + + buildFile << """ + plugins { + id 'com.netflix.nebula.ospackage' + } + + ospackage { + packageName = 'multi-format-test' + version = '1.0.0' + release = '1' + + from('src') { + into '/opt/app' + } + } + """.stripIndent() + + when: 'run both tasks first time' + def result1 = runTasks('buildDeb', 'buildRpm', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run both tasks second time' + def result2 = runTasks('buildDeb', 'buildRpm', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'deb package is created' + new File(projectDir, 'build/distributions/multi-format-test_1.0.0-1_all.deb').exists() + + and: 'rpm package is created' + new File(projectDir, 'build/distributions/multi-format-test-1.0.0-1.noarch.rpm').exists() + } + + def 'deb with explicit file permissions works with configuration cache'() { + given: + def srcDir = new File(projectDir, 'src') + srcDir.mkdirs() + new File(srcDir, 'app.sh').text = '#!/bin/sh\necho "hello"' + new File(srcDir, 'config.txt').text = 'config value' + + buildFile << """ + plugins { + id 'com.netflix.nebula.deb' + } + + tasks.register('myDeb', com.netflix.gradle.plugins.deb.Deb) { + packageName = 'permissions-test' + version = '1.0.0' + + from('src') { + into '/opt/app' + include 'app.sh' + filePermissions { + unix(0755) + } + } + from('src') { + into '/opt/app' + include 'config.txt' + filePermissions { + unix(0644) + } + } + } + """.stripIndent() + + when: 'run myDeb first time' + def result1 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is stored' + result1.output.contains('Configuration cache entry stored') + + when: 'run myDeb second time' + def result2 = runTasks('myDeb', '--configuration-cache') + + then: 'configuration cache is reused' + result2.output.contains('Configuration cache entry reused') + + and: 'package is created' + new File(projectDir, 'build/distributions/permissions-test_1.0.0_all.deb').exists() + } + + /** + * Documents the known configuration cache limitation of OspackageApplicationSpringBootPlugin. + * + * The Spring Boot plugin uses project.distributions.main.contents.exclude { } with a closure + * that captures a Provider derived from project configurations. While the core + * ospackage tasks are CC-compatible (Task.getProject() at execution time is fixed), the Spring + * Boot integration plugin adds configuration-time closures that may not survive CC serialization + * depending on the Gradle and Spring Boot versions in use. + * + * To verify Spring Boot CC support manually: + * 1. Create a project with `org.springframework.boot` and `com.netflix.nebula.ospackage-application-spring-boot` + * 2. Run: ./gradlew buildDeb --configuration-cache + * 3. Check for CC incompatibility problems in the output + */ + @Ignore('Requires a full Spring Boot project setup with the org.springframework.boot plugin') + def 'spring boot ospackage plugin configuration cache compatibility'() { + given: + buildFile << """ + plugins { + id 'org.springframework.boot' version '3.5.2' + id 'com.netflix.nebula.ospackage-application-spring-boot' + } + + application { + mainClass = 'com.example.Main' + } + """.stripIndent() + + when: + def result = runTasks('buildDeb', '--configuration-cache') + + then: + result.output.contains('Configuration cache entry stored') + } +} diff --git a/src/test/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPluginSpec.groovy b/src/test/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPluginSpec.groovy index a43b2c6a..b8c40fec 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPluginSpec.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/daemon/OspackageDaemonPluginSpec.groovy @@ -38,7 +38,7 @@ class OspackageDaemonPluginSpec extends PluginProjectSpec { then: noExceptionThrown() project.tasks.withType(DaemonTemplateTask) { - assert it.context.daemonName == project.name + assert it.context.get().daemonName == project.name } } diff --git a/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateHelperSpec.groovy b/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateHelperSpec.groovy index b7627ef4..1983e6fa 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateHelperSpec.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateHelperSpec.groovy @@ -1,6 +1,5 @@ package com.netflix.gradle.plugins.daemon -import org.gradle.api.Project import org.junit.Rule import org.junit.rules.TemporaryFolder import spock.lang.Specification @@ -17,8 +16,7 @@ class TemplateHelperSpec extends Specification { def 'fails if file is not present'() { given: - Project p = Mock(Project) - TemplateHelper templateHelper = new TemplateHelper(destinationFolder.root, tmpFolder.root.path, p) + TemplateHelper templateHelper = new TemplateHelper(destinationFolder.root, tmpFolder.root.path, tmpFolder.root) when: templateHelper.generateFile("test", [:]) @@ -28,21 +26,18 @@ class TemplateHelperSpec extends Specification { } def 'generates files does not fail with valid templates'() { - Project p = Mock(Project) - File initd = tmpFolder.newFile('initd.tpl') - initd.text = """ + // Use a unique template name that won't conflict with classpath resources + File testTemplate = tmpFolder.newFile('test-custom-template.tpl') + testTemplate.text = """ #!/bin/sh """ - TemplateHelper templateHelper = new TemplateHelper(destinationFolder.root, tmpFolder.root.path, p) + TemplateHelper templateHelper = new TemplateHelper(destinationFolder.root, "", tmpFolder.root) when: - templateHelper.generateFile("initd", [:]) + templateHelper.generateFile("test-custom-template", [:]) then: - interaction { - p.file(_) >> initd - } notThrown(FileNotFoundException) } } diff --git a/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateSyntaxSpec.groovy b/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateSyntaxSpec.groovy index c0ef016e..f13869fe 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateSyntaxSpec.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/daemon/TemplateSyntaxSpec.groovy @@ -17,17 +17,15 @@ package com.netflix.gradle.plugins.daemon import nebula.test.ProjectSpec -import org.gradle.api.Project class TemplateSyntaxSpec extends ProjectSpec { def 'each template validates'() { given: - Project p = Mock(Project) def plugin = new OspackageDaemonPlugin() plugin .defaultDefinition = new DefaultDaemonDefinitionExtension() def templates = ['initd', 'log-run', 'run'] - def helper = new TemplateHelper(projectDir, '/com/netflix/gradle/plugins/daemon', p) + def helper = new TemplateHelper(projectDir, '/com/netflix/gradle/plugins/daemon', projectDir) DaemonDefinition definition = new DaemonDefinition() def context = plugin.toContext(plugin.getDefaultDaemonDefinition(false), definition) context['isRedhat'] = 'true' @@ -45,8 +43,7 @@ class TemplateSyntaxSpec extends ProjectSpec { } def 'template fails with a null'() { given: - Project p = Mock(Project) - def helper = new TemplateHelper(projectDir, '/com/netflix/gradle/plugins/daemon', p) + def helper = new TemplateHelper(projectDir, '/com/netflix/gradle/plugins/daemon', projectDir) def context = [:] context['isRedhat'] = true diff --git a/src/test/groovy/com/netflix/gradle/plugins/deb/DebPluginTest.groovy b/src/test/groovy/com/netflix/gradle/plugins/deb/DebPluginTest.groovy index 4c3e826a..7b782d64 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/deb/DebPluginTest.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/deb/DebPluginTest.groovy @@ -1046,10 +1046,10 @@ class DebPluginTest extends ProjectSpec { 'Foo': 'bar', 'baz': 'quux' ]) - customFields << [ + customField([ 'beep': 'boop', 'Blip': 'boing' - ] + ]) }) debTask.from(srcDir) diff --git a/src/test/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGeneratorSpec.groovy b/src/test/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGeneratorSpec.groovy index 130bd618..dff68749 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGeneratorSpec.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/deb/MaintainerScriptsGeneratorSpec.groovy @@ -28,7 +28,7 @@ class MaintainerScriptsGeneratorSpec extends ProjectSpec { generator.generate(context) then: - 1 * fileSystemActions.copy(source, new File(destination, 'preinst')) + 1 * fileSystemActions.copy(_ as File, new File(destination, 'preinst')) 0 * templateHelper.generateFile('preinst', _ as Map) } @@ -60,7 +60,7 @@ class MaintainerScriptsGeneratorSpec extends ProjectSpec { generator.generate(context) then: - 1 * fileSystemActions.copy(source, new File(destination, 'postinst')) + 1 * fileSystemActions.copy(_ as File, new File(destination, 'postinst')) 0 * templateHelper.generateFile('postinst', _ as Map) } @@ -108,7 +108,7 @@ class MaintainerScriptsGeneratorSpec extends ProjectSpec { generator.generate(context) then: - 1 * fileSystemActions.copy(source, new File(destination, 'prerm')) + 1 * fileSystemActions.copy(_ as File, new File(destination, 'prerm')) 0 * templateHelper.generateFile('prerm', _ as Map) } @@ -140,7 +140,7 @@ class MaintainerScriptsGeneratorSpec extends ProjectSpec { generator.generate(context) then: - 1 * fileSystemActions.copy(source, new File(destination, 'postrm')) + 1 * fileSystemActions.copy(_ as File, new File(destination, 'postrm')) 0 * templateHelper.generateFile('postrm', _ as Map) } diff --git a/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePluginTest.groovy b/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePluginTest.groovy index b1e6294b..8e7dcf3c 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePluginTest.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingBasePluginTest.groovy @@ -42,7 +42,7 @@ class SystemPackagingBasePluginTest extends ProjectSpec { project.apply plugin: 'com.netflix.nebula.ospackage-base' project.ospackage { - release = 3 + release = '3' into '/opt/bleah' os = LINUX from(srcDir) @@ -75,7 +75,7 @@ class SystemPackagingBasePluginTest extends ProjectSpec { ProjectPackagingExtension ext = project.getExtensions().getByType(ProjectPackagingExtension) ext.with { provides project.name - release = 3 + release = '3' requires 'awesomesauce' url 'http://notawesome.com' into '/opt/bleah' diff --git a/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtensionTest.groovy b/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtensionTest.groovy index 011e380e..76bb44b1 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtensionTest.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/packaging/SystemPackagingExtensionTest.groovy @@ -1,9 +1,10 @@ package com.netflix.gradle.plugins.packaging +import org.gradle.testfixtures.ProjectBuilder import spock.lang.Specification class SystemPackagingExtensionTest extends Specification { - SystemPackagingExtension extension = new SystemPackagingExtension() + SystemPackagingExtension extension = ProjectBuilder.builder().build().objects.newInstance(SystemPackagingExtension) def "Can define required package name without version and flag"() { given: @@ -13,8 +14,8 @@ class SystemPackagingExtensionTest extends Specification { extension.requires(packageName) then: - extension.dependencies.size() == 1 - Dependency dep = extension.dependencies[0] + extension.dependencies.get().size() == 1 + Dependency dep = extension.dependencies.get()[0] dep.packageName == packageName dep.version == '' dep.flag == 0 @@ -28,8 +29,8 @@ class SystemPackagingExtensionTest extends Specification { extension.requires(packageName, '1.0.0') then: - extension.dependencies.size() == 1 - Dependency dep = extension.dependencies[0] + extension.dependencies.get().size() == 1 + Dependency dep = extension.dependencies.get()[0] dep.packageName == packageName dep.version == '1.0.0' dep.flag == 0 @@ -43,8 +44,8 @@ class SystemPackagingExtensionTest extends Specification { extension.requires(packageName, '1.0.0', 5) then: - extension.dependencies.size() == 1 - Dependency dep = extension.dependencies[0] + extension.dependencies.get().size() == 1 + Dependency dep = extension.dependencies.get()[0] dep.packageName == packageName dep.version == '1.0.0' dep.flag == 5 @@ -323,8 +324,8 @@ class SystemPackagingExtensionTest extends Specification { extension.triggerInstall(file, packageName) then: - extension.triggerInstallCommands.size() == 1 - Trigger trig = extension.triggerInstallCommands[0] + extension.triggerInstallCommands.get().size() == 1 + Trigger trig = extension.triggerInstallCommands.get()[0] trig.command == file trig.dependency.packageName == packageName trig.dependency.version == '' @@ -366,8 +367,8 @@ class SystemPackagingExtensionTest extends Specification { extension.triggerUninstall(file, packageName) then: - extension.triggerUninstallCommands.size() == 1 - Trigger trig = extension.triggerUninstallCommands[0] + extension.triggerUninstallCommands.get().size() == 1 + Trigger trig = extension.triggerUninstallCommands.get()[0] trig.command == file trig.dependency.packageName == packageName trig.dependency.version == '' @@ -409,8 +410,8 @@ class SystemPackagingExtensionTest extends Specification { extension.triggerPostUninstall(file, packageName) then: - extension.triggerPostUninstallCommands.size() == 1 - Trigger trig = extension.triggerPostUninstallCommands[0] + extension.triggerPostUninstallCommands.get().size() == 1 + Trigger trig = extension.triggerPostUninstallCommands.get()[0] trig.command == file trig.dependency.packageName == packageName trig.dependency.version == '' diff --git a/src/test/groovy/com/netflix/gradle/plugins/rpm/RpmPluginIntegrationTest.groovy b/src/test/groovy/com/netflix/gradle/plugins/rpm/RpmPluginIntegrationTest.groovy index 12205f3e..f514d90c 100644 --- a/src/test/groovy/com/netflix/gradle/plugins/rpm/RpmPluginIntegrationTest.groovy +++ b/src/test/groovy/com/netflix/gradle/plugins/rpm/RpmPluginIntegrationTest.groovy @@ -367,8 +367,6 @@ task buildRpm(type: Rpm) { FileUtils.forceMkdirParent(bananaFile) bananaFile.text = 'banana' - debug = true - buildFile << """ plugins { id 'com.netflix.nebula.ospackage'