Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Keep permisions of additionalResources on packaging [Linux] #155

Closed
2 of 5 tasks
Ali-RS opened this issue Feb 23, 2022 · 20 comments
Closed
2 of 5 tasks

Keep permisions of additionalResources on packaging [Linux] #155

Ali-RS opened this issue Feb 23, 2022 · 20 comments
Labels
bug Something isn't working feedback Waiting for feedback fixed Issue fixed and release pending

Comments

@Ali-RS
Copy link

Ali-RS commented Feb 23, 2022

Hello!

I'm submitting a…

  • bug report
  • feature request
  • other

Short description of the issue/suggestion:
In the packageMyAppForLinux task I have added a file into additionalResources to be included in the package. The original file is marked as executable (chmod +x) but when packaged into the tarball it does not keep that permission.

Can you please help me to solve this issue?

What is the expected behavior?

I am expecting the packaged file to keep the permissions of its original file. (In my case the executable permission)

What is the current behavior?

It does not keep the original file permissions. (In my case the executable permission)

Do you have outputs, screenshots, demos or samples which demonstrate the problem or enhancement?

Here is my packageMyAppForLinux task:

The file blue-cube.desktop is the one I want to preserve its permission when packaging.

task packageMyAppForLinux(type: io.github.fvarrui.javapackager.gradle.PackageTask, dependsOn: build) {
    platform = io.github.fvarrui.javapackager.model.Platform.linux
    createTarball = true
    outputDirectory = file("$buildDir/bundles/linux-x64")
    additionalResources = javapackager.additionalResources + [file('blue-cube.desktop')]
}

Please tell us about your environment:

  • JavaPackager version: 1.6.3
  • OS version: Linux Mint 20.1 Ulyssa
  • JDK version: 14
  • Build tool:
    • Maven
    • Gradle

Regards

@fvarrui
Copy link
Owner

fvarrui commented Feb 23, 2022

Hi @Ali-RS!

JavaPackager is using Apache Commons FileUtils to copy additional files/folders, which doesn't copy permissions, only content:

/**
* Copy a list of resources to a folder
*
* @param resources List of files and folders to be copied
* @param destination Destination folder. All specified resources will be copied
* here
*/
protected void copyAdditionalResources(List<File> resources, File destination) {
Logger.infoIndent("Copying additional resources");
resources.stream().forEach(r -> {
if (!r.exists()) {
Logger.warn("Additional resource " + r + " doesn't exist");
return;
}
try {
if (r.isDirectory()) {
FileUtils.copyFolderToFolder(r, destination);
} else if (r.isFile()) {
FileUtils.copyFileToFolder(r, destination);
}
} catch (Exception e) {
Logger.error(e.getMessage(), e);
}
});
// copy bootstrap script
if (FileUtils.exists(getScripts().getBootstrap())) {
String scriptExtension = getExtension(getScripts().getBootstrap().getName());
File scriptsFolder = new File(destination, "scripts");
bootstrapFile = new File(scriptsFolder, "bootstrap" + (!scriptExtension.isEmpty() ? "." + scriptExtension : ""));
try {
FileUtils.copyFileToFile(getScripts().getBootstrap(), bootstrapFile);
bootstrapFile.setExecutable(true, false);
} catch (Exception e) {
Logger.error(e.getMessage(), e);
}
}
Logger.infoUnindent("All additional resources copied!");
}

Then, when creating the tarball, permissions are set only for those files that JavaPackager considers executable: like the app binary, jre/bin/* (if we bundle a JRE) and bootstrap scripts:

// if zipball is for linux platform
else if (Platform.linux.equals(platform)) {
tarTask.from(appFolder.getParentFile(), copySpec -> {
copySpec.include(appFolder.getName() + "/**");
copySpec.exclude(appFolder.getName() + "/" + executable.getName());
copySpec.exclude(appFolder.getName() + "/" + jreDirectoryName + "/bin/*");
copySpec.exclude(appFolder.getName() + "/scripts/*");
});
tarTask.from(appFolder.getParentFile(), copySpec -> {
copySpec.include(appFolder.getName() + "/" + executable.getName());
copySpec.include(appFolder.getName() + "/" + jreDirectoryName + "/bin/*");
copySpec.include(appFolder.getName() + "/scripts/*");
copySpec.setFileMode(0755);
});
}

In the case of Gradle, this behaviour is hardcoded ... in Maven, you could make your own VTL template to create a customized tarball.

<fileSets>
<fileSet>
<outputDirectory>${info.name}</outputDirectory>
<directory>${info.outputDirectory}/${info.name}</directory>
<excludes>
<exclude>${info.executable.name}</exclude>
#if ($info.bundleJre)
<exclude>${info.jreDirectoryName}/bin/*</exclude>
<exclude>${info.jreDirectoryName}/lib/jspawnhelper</exclude>
<exclude>scripts/*</exclude>
#end
</excludes>
</fileSet>
<fileSet>
<outputDirectory>${info.name}</outputDirectory>
<directory>${info.outputDirectory}/${info.name}</directory>
<includes>
<include>${info.executable.name}</include>
#if ($info.bundleJre)
<include>${info.jreDirectoryName}/bin/*</include>
<include>${info.jreDirectoryName}/lib/jspawnhelper</include>
<include>scripts/*</include>
#end
</includes>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>

Options:

  1. Including a new option in JavaPackager to specify which additional resources must have execution permissions (this should work on any platform).
  2. Copying files on Linux/MacOS using an alternative method, which allows to copy the permissions of the original file (this only would work on Linux/MacOS, but not on Windows, as the only way to specify those permissions on Windows is inside the tarball).

@Ali-RS
Copy link
Author

Ali-RS commented Feb 23, 2022

I see, thanks for explaining. Because in my case it is a single file it is easy to manually change permissions after packaging.

Copying files on Linux/MacOS using an alternative method, which allows to copy the permissions of the original file

Maybe you can use java.nio.file.Files.

Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES)

@fvarrui should I keep this issue open?

@fvarrui
Copy link
Owner

fvarrui commented Feb 23, 2022

I see, thanks for explaining. Because in my case it is a single file it is easy to manually change permissions after packaging.

Copying files on Linux/MacOS using an alternative method, which allows to copy the permissions of the original file

Maybe you can use java.nio.file.Files.

Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES)

I've not tested yet, but I read about this method, and it seems not to be working on MacOS. But could be a first and easy approach.

@fvarrui should I keep this issue open?

Yes, please! I think it's an interesting question.

@fvarrui
Copy link
Owner

fvarrui commented Feb 23, 2022

Option 2 released to issue-155 branch as JavaPackager 1.6.4-SNAPSHOT (you have to install it manually to your local repo). Now it's using java.nio.file.Files.copy(...):

public static void copyFileToFolder(File source, File destFolder, boolean overwrite) throws Exception {
Logger.info("Copying file [" + source + "] to folder [" + destFolder + "]");
if (new File(destFolder, source.getName()).exists() && !overwrite) return;
try {
//copyFileToDirectory(source, destFolder);
Files.copy(source.toPath(), new File(destFolder, source.getName()).toPath(), StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
throw new Exception(e.getMessage(), e);
}
}

I've tested on Linux and Windows, and it seems to be working fine (remember that when packaging from Windows to Linux/MacOS, additional resources will not have the expected permissions). Try it and give me some feedback, please!

@Ali-RS
Copy link
Author

Ali-RS commented Feb 23, 2022

Cool, will try it soon. Thank you so much!

@Ali-RS
Copy link
Author

Ali-RS commented Feb 23, 2022

Tested it on Linux and it works like charm! Thanks @fvarrui

@Ali-RS Ali-RS closed this as completed Feb 23, 2022
@fvarrui
Copy link
Owner

fvarrui commented Feb 23, 2022

Branch issue-155 merged into devel.

@fvarrui
Copy link
Owner

fvarrui commented Mar 3, 2022

JavaPackager 1.6.4 released to Maven Central.

@Ali-RS
Copy link
Author

Ali-RS commented Mar 3, 2022

Thanks!

@maths22
Copy link
Contributor

maths22 commented Mar 22, 2022

Sadly Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES) seems to not preserve permissions on macos. I'm not sure what the best solution is (maybe option 1, but that feels kinda gross?)

@fvarrui
Copy link
Owner

fvarrui commented Mar 22, 2022

😞

@fvarrui fvarrui reopened this Mar 22, 2022
@Ali-RS
Copy link
Author

Ali-RS commented Mar 22, 2022

@maths22 what java version are you using?

l found a similar bug report at https://bugs.openjdk.java.net/browse/JDK-8204848 but seems it is already resolved.

@maths22
Copy link
Contributor

maths22 commented Mar 22, 2022

I'm on java 17. It doesn't throw an error; it just also doesn't preserve permissions.

@fvarrui
Copy link
Owner

fvarrui commented Mar 22, 2022

A quick fix would be to check if the original file has execute permissions, set them to the copied file:

public static void copyFileToFolder(File source, File destFolder, boolean overwrite) throws Exception {
	Logger.info("Copying file [" + source + "] to folder [" + destFolder + "]");
	if (new File(destFolder, source.getName()).exists() && !overwrite) return;
	try {
		//copyFileToDirectory(source, destFolder);
		File destFile = new File(destFolder, source.getName());
		Files.copy(source.toPath(), destFile.toPath(), StandardCopyOption.COPY_ATTRIBUTES);
		if (source.canExecute()) {
			destFile.setExecutable(true, false); // set execution permissions for all (ugo+x)
		}
	} catch (IOException e) {
		throw new Exception(e.getMessage(), e);
	}
}

I've applied this change for JavaPackager 1.6.6.-SNAPSHOT and published in issue-155 branch. Please, could you test it and give some feedback?

Thanks!

@maths22
Copy link
Contributor

maths22 commented Mar 22, 2022

That didn't work for me, probably because that only checks the permissions of the parent file, and if the parent file is a directory it's not going to check and set permissions on the child files

@fvarrui
Copy link
Owner

fvarrui commented Mar 22, 2022

Mmmmh ... yeah! you are right ... this change only works when an additionalResource is a file, but not when is a directory. I have to improve FileUtils.copyFolderToFolder(), used when additional resources are directories.

@fvarrui
Copy link
Owner

fvarrui commented Mar 23, 2022

This is what JavaPackager does when copying additional resources:

	protected void copyAdditionalResources(List<File> resources, File destination) {

                [...]

		resources.stream().forEach(r -> {
			if (!r.exists()) {
				Logger.warn("Additional resource " + r + " doesn't exist");
				return;
			}
			try {
				if (r.isDirectory()) {
					FileUtils.copyFolderToFolder(r, destination);
				} else if (r.isFile()) {
					FileUtils.copyFileToFolder(r, destination);
				}
			} catch (Exception e) {
				Logger.error(e.getMessage(), e);
			}
		});

                [...]

	}

Whay if we use the copy native command on MacOS and Linux?

	public static void copyFileToFolder(File source, File destFolder, boolean overwrite) throws Exception {
		Logger.info("Copying file [" + source + "] to folder [" + destFolder + "]");
		if (new File(destFolder, source.getName()).exists() && !overwrite) return;
		try {
			File destFile = new File(destFolder, source.getName());
			if (Platform.windows.isCurrentPlatform())
				Files.copy(source.toPath(), destFile.toPath(), StandardCopyOption.COPY_ATTRIBUTES);
			else {
				CommandUtils.execute("cp", source, destFile);
			}			
		} catch (IOException e) {
			throw new Exception(e.getMessage(), e);
		}
	}
	
	public static void copyFolderToFolder(File from, File to) throws Exception {
		Logger.info("Copying folder [" + from + "] to folder [" + to + "]");
		if (!from.isDirectory()) throw new Exception("Source folder " + from + " is not a directory");
		try {
			if (Platform.windows.isCurrentPlatform())
				copyDirectoryToDirectory(from, to);
			else {
				CommandUtils.execute("cp", "-R", from, to);
			}
		} catch (IOException e) {
			throw new Exception(e.getMessage(), e);
		}
	}

so, it would keep the permissions of the original files

@Ali-RS
Copy link
Author

Ali-RS commented Mar 23, 2022

Whay if we use the copy native command on MacOS and Linux?

Yes, sounds good to me.

@fvarrui
Copy link
Owner

fvarrui commented Mar 23, 2022

Changes published in issue-155 branch.

@fvarrui fvarrui added bug Something isn't working feedback Waiting for feedback fixed Issue fixed and release pending labels Mar 25, 2022
@fvarrui
Copy link
Owner

fvarrui commented Apr 5, 2022

JavaPackager v1.6.6 released to Maven Central

@fvarrui fvarrui closed this as completed Apr 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working feedback Waiting for feedback fixed Issue fixed and release pending
Projects
None yet
Development

No branches or pull requests

3 participants