-
Notifications
You must be signed in to change notification settings - Fork 11
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
Add new feature Delete Artifacts with pattern #84
base: master
Are you sure you want to change the base?
Changes from all commits
7fb64bb
a93e9ea
3bcaa86
acbbd07
ac83d8e
55dd7f7
9396187
54a777f
ce8d152
5cafa92
3eb31f6
4910b77
0ef4dc2
e88f034
b2e7791
c55c64a
4d22638
32a06f9
32b604c
96ae7c0
28c1cff
011f4bd
dbac7b9
ae84063
624e895
aa3a550
dbbc5a0
45ce8f8
69f3fbc
ec2cf6e
d291e2d
4833d65
76cde80
95bef37
8835036
3f158e4
82e63cb
7d6d452
e04ddab
fd94659
6d45cbd
0116e54
8179064
79c6ae0
5ebf960
69acfd9
3d0a43f
0eb7ade
5ca1edf
cb5d393
64f8c5f
59dc567
6dea4b0
b634e5f
36841e3
6a745a1
ddccd42
1634138
d9d9dd6
419199f
b81478c
6c770af
a835a0c
ddb53e2
d806f3e
f569791
3ff8e6e
3bedd1a
f553893
237fbf3
470eb42
ebc8038
8349e7c
b63907f
4eee00c
6343df5
0be8337
dfd1641
47d3685
e6f1c1e
3c6c139
f20872c
e87e078
c639f99
3db4cad
b416844
e471acb
1cbee8f
d47dcf8
2658aa7
a9b7d7e
4cfe033
353fed8
45c5610
3b47a72
41cef69
42d0cf4
4708128
ec2d1dd
d32bfac
ac49b59
39ad85b
3b20420
e03a4c2
5cf758f
6d8605b
7379491
02c810c
0628225
9798873
9de4746
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
/.idea | ||
**/*.iml | ||
target/ | ||
work/ | ||
.vscode/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,6 +215,45 @@ pipeline { | |
} | ||
``` | ||
|
||
#### Delete build artifacts matching patterns | ||
The following configuration has a single rule that utilizes the `Match every build` condition and deletes specific build artifacts based on Ant-style glob patterns defined in the `Include Patterns/Exclude Patterns`. | ||
|
||
Here's an example using the include pattern `"**"`, exclude pattern `"**/*.log"` to delete all files except log files: | ||
|
||
```groovy | ||
properties( | ||
[ | ||
buildDiscarder(BuildHistoryManager( | ||
[ | ||
[ | ||
actions: [DeleteArtifactsMatchingPatterns(excludePatterns: '**/*.log', includePatterns: '**')], | ||
conditions: [MatchEveryBuild()] | ||
] | ||
] | ||
) | ||
) | ||
] | ||
) | ||
|
||
node { | ||
|
||
sh ''' | ||
rm -rf * | ||
touch test.xml | ||
touch testLog.log | ||
touch testTxt.txt | ||
mkdir -p testFolder | ||
touch testFolder/test1.xml | ||
touch testFolder/testLog1.log | ||
touch testFolder/testTxt1.txt | ||
''' | ||
archiveArtifacts artifacts: '**', followSymlinks: false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... I guess this is valuable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is the changes in wiki, because Github does not allow to send pull request to the wiki. https://github.com/nurgul212/build-history-manager-plugin-wiki/compare/artifacts-pattern?expand=1 In wiki, |
||
} | ||
``` | ||
|
||
#### Configuration for delete artifacts matching include and exclude patterns | ||
 | ||
|
||
## Wiki | ||
Please refer to the [Wiki](https://github.com/jenkinsci/build-history-manager-plugin/wiki) | ||
for more detailed information. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package pl.damianszczepanik.jenkins.buildhistorymanager.descriptors.actions; | ||
|
||
import hudson.Extension; | ||
import hudson.model.Descriptor; | ||
import org.jenkinsci.Symbol; | ||
import pl.damianszczepanik.jenkins.buildhistorymanager.model.actions.Action; | ||
import pl.damianszczepanik.jenkins.buildhistorymanager.model.actions.DeleteArtifactsMatchingPatternsAction; | ||
|
||
/** | ||
* Descriptor implementation needed to render UI for {@link DeleteArtifactsMatchingPatternsAction}. | ||
*/ | ||
@Extension | ||
@Symbol("DeleteArtifactsMatchingPatterns") | ||
public class DeleteArtifactsMatchingPatternsActionDescriptor extends Descriptor<Action>{ | ||
|
||
public DeleteArtifactsMatchingPatternsActionDescriptor() { | ||
super(DeleteArtifactsMatchingPatternsAction.class); | ||
} | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return "Delete artifacts matching patterns"; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package pl.damianszczepanik.jenkins.buildhistorymanager.model.actions; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.DirectoryStream; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.Collection; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import org.jenkinsci.remoting.RoleChecker; | ||
import org.kohsuke.stapler.DataBoundConstructor; | ||
import org.kohsuke.stapler.DataBoundSetter; | ||
|
||
import hudson.FilePath; | ||
import hudson.FilePath.FileCallable; | ||
import hudson.Util; | ||
import hudson.model.Run; | ||
import hudson.remoting.VirtualChannel; | ||
import jenkins.util.VirtualFile; | ||
|
||
/** | ||
* Deletes the artifacts matching patterns. | ||
*/ | ||
public class DeleteArtifactsMatchingPatternsAction extends Action { | ||
private static final Logger LOG = Logger.getLogger(DeleteArtifactsMatchingPatternsAction.class.getName()); | ||
|
||
private String includePatterns; | ||
private String excludePatterns; | ||
|
||
@DataBoundConstructor | ||
public DeleteArtifactsMatchingPatternsAction() { | ||
// Jenkins stapler requires to have public constructor with @DataBoundConstructor | ||
} | ||
|
||
public String getIncludePatterns() { | ||
return includePatterns; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setIncludePatterns(String includePatterns) { | ||
this.includePatterns = includePatterns; | ||
} | ||
|
||
public String getExcludePatterns() { | ||
return excludePatterns; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setExcludePatterns(String excludePatterns) { | ||
this.excludePatterns = excludePatterns; | ||
} | ||
|
||
// if 'file' is on a different node, this FileCallable will be transferred to that node and executed there. | ||
public static final class DeleteFileCallable implements FileCallable<Void> { | ||
private static final long serialVersionUID = 2469669731898204125L; | ||
private final File archiveRootDirectory; | ||
|
||
public DeleteFileCallable(File archiveRootDirectory) { | ||
this.archiveRootDirectory = archiveRootDirectory; | ||
} | ||
|
||
@Override | ||
public Void invoke(File file, VirtualChannel channel) throws IOException { | ||
deleteFileOrLogError(file); | ||
return null; | ||
} | ||
|
||
/** | ||
* Deletes the specified directory if it is empty and its parent is the archive root directory. | ||
* Additionally, deletes the parent directories recursively. | ||
* | ||
* @param directory The directory to be deleted. | ||
* @throws IOException If an I/O error occurs. | ||
*/ | ||
void deleteEmptyDirectoryAndParent(File directory) throws IOException { | ||
if (isDirectoryEmpty(directory.toPath()) && isArchiveRootDirectory(directory.getParentFile())) { | ||
Util.deleteFile(directory); | ||
deleteParentDirectories(directory.getParentFile()); | ||
} | ||
} | ||
|
||
boolean isDirectoryEmpty(Path path) throws IOException { | ||
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path)) { | ||
return !directoryStream.iterator().hasNext(); | ||
} | ||
} | ||
|
||
boolean isArchiveRootDirectory(File directory) { | ||
return this.archiveRootDirectory.equals(directory); | ||
} | ||
|
||
void deleteParentDirectories(File directory) { | ||
while (!isArchiveRootDirectory(directory)) { | ||
deleteDirectory(directory); | ||
directory = directory.getParentFile(); | ||
} | ||
} | ||
|
||
void deleteDirectory(File directory) { | ||
boolean deleteSuccess = directory.delete(); | ||
if(!deleteSuccess) { | ||
LOG.log(Level.FINE, "Deletion of directory failed: " + directory.getAbsolutePath()); | ||
} | ||
} | ||
|
||
void deleteFileOrLogError(File file) throws IOException { | ||
if (file.isFile()) { | ||
Util.deleteFile(file); | ||
deleteEmptyDirectoryAndParent(file.getParentFile()); | ||
return; | ||
} | ||
LOG.log(Level.FINE, file + " is neither a directory nor a regular file."); | ||
} | ||
|
||
/** | ||
* Overrides the 'checkRoles' method to fulfill the requirement of implementing RoleSensitive. | ||
* | ||
* @param checker The RoleChecker used for access control and security purposes. | ||
* @throws SecurityException If there are security-related issues. | ||
*/ | ||
@Override | ||
public void checkRoles(RoleChecker checker) throws SecurityException { | ||
//Nothing is being done here even though a call to RoleChecker#check(…) is expected. | ||
} | ||
} | ||
|
||
@Override | ||
public void perform(Run<?, ?> run) throws IOException, InterruptedException { | ||
VirtualFile virtualRoot = run.getArtifactManager().root(); | ||
Collection<String> files = virtualRoot.list(includePatterns, excludePatterns, false); | ||
LOG.log(Level.FINE, "Include Pattern Files: " + files); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you check in logs what does it print? probably not what you expected :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ping |
||
|
||
for (String path : files) { | ||
deleteFileAtPath(virtualRoot, path); | ||
} | ||
} | ||
|
||
public void deleteFileAtPath(VirtualFile virtualRoot, String path) throws IOException, InterruptedException { | ||
VirtualFile virtualFile = virtualRoot.child(path); | ||
File file = new File(virtualFile.toURI().getPath()); | ||
FilePath filePath = new FilePath(file); | ||
DeleteFileCallable deleteFileCallableInstance = new DeleteFileCallable(new File(virtualRoot.toURI())); | ||
filePath.act(deleteFileCallableInstance); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?jelly escape-by-default='true'?> | ||
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form"> | ||
<f:entry title="Patterns to include" field="includePatterns"> | ||
<f:textbox /> | ||
</f:entry> | ||
<f:entry title="Patterns to exclude" field="excludePatterns"> | ||
<f:textbox /> | ||
</f:entry> | ||
</j:jelly> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<p><b>Description</b></p> | ||
<p>Deletes build artifacts that match the Ant-style glob patterns provided.</p> | ||
<p><b>Include Patterns</b></p> | ||
<p>The include pattern is a comma-separated list of Ant-style globs that follows a specific format. | ||
The path separator for this format is the forward slash (/) and an empty string is used in the include pattern to indicate that there are no matches. | ||
If the goal is to match everything except for certain files (defined as "Exclude Patterns"), then the recommended approach is to use the value <code>**</code> here and call out exclusions below.</p> | ||
<p><b>Exclude Patterns</b></p> | ||
<p>The exclude pattern is an optional input that should be in the same format as the include pattern.</p> | ||
<p><b>Use cases</b></p> | ||
<p>To delete artifacts with a specific file extension, for example use the include pattern "**/*.log" to delete all the log files.</p> | ||
<p>To keep artifacts with a specific file extension, for example use the exclude pattern "**/*.log" to delete everything except the log files.</p> | ||
|
||
<p><b>Warning!</b></p> | ||
<p>It is important to note that this feature should be used with caution as it can be permanently | ||
delete artifacts that may be important for later reference or analysis. It is recommended that users | ||
carefully review the pattern and build history before executing the deletion.</p> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package pl.damianszczepanik.jenkins.buildhistorymanager.descriptors.actions; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import hudson.model.AbstractDescribableImpl; | ||
import hudson.model.Descriptor; | ||
import org.jenkinsci.plugins.structs.SymbolLookup; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.jvnet.hudson.test.JenkinsRule; | ||
|
||
public class DeleteArtifactsMatchingPatternsActionDescriptorTest { | ||
|
||
@Rule | ||
public JenkinsRule j = new JenkinsRule(); | ||
nurgul212 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Test | ||
public void getDisplayName_ReturnsDescriptorName() { | ||
|
||
// given | ||
Descriptor descriptor = SymbolLookup.get().findDescriptor(AbstractDescribableImpl.class, "DeleteArtifactsMatchingPatterns"); | ||
|
||
// when | ||
String displayName = descriptor.getDisplayName(); | ||
|
||
// then | ||
assertThat(displayName).isEqualTo("Delete artifacts matching patterns"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is not clear to me what is the intention of this code snipped in readme file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a shell command that removes all files and directories in the current directory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know what it does but I don't understand why did you add this code here? What is the goal? Are you expecting that someone will copy&paste that code into his pipeline ?