3131import java .nio .file .Files ;
3232import java .nio .file .Path ;
3333import java .nio .file .Paths ;
34+ import java .nio .file .attribute .FileTime ;
35+ import java .time .Instant ;
36+ import java .util .HashSet ;
37+ import java .util .Set ;
3438
3539public class ZipOutputHandler implements OutputHandler {
3640
3741 private final ZipArchiveOutputStream zipOutputStream ;
3842 private final File zip ;
3943 private final String directory ;
44+ private final Set <String > createdDirs = new HashSet <>();
4045
4146 public ZipOutputHandler (Project project ) throws IOException {
4247 File baseDirectory = new File ("." ).getCanonicalFile ();
@@ -78,10 +83,19 @@ public boolean exists(String path) {
7883
7984 @ Override
8085 public void write (String path , Template contents ) throws IOException {
81- ZipArchiveEntry zipEntry = new ZipArchiveEntry (directory != null ? StringUtils .prependUri (directory , path ) : path );
82- if (contents .isExecutable ()) {
83- zipEntry .setUnixMode (UnixStat .FILE_FLAG | 0755 );
84- }
86+ String entryName = (directory != null ? StringUtils .prependUri (directory , path ) : path );
87+ FileTime lastModified = FileTime .from (Instant .now ());
88+
89+ // ensure parent directories exist as explicit dir entries
90+ // https://github.com/apache/grails-core/issues/15186
91+ createParentDirs (entryName , lastModified );
92+
93+ ZipArchiveEntry zipEntry = new ZipArchiveEntry (entryName );
94+ setZipEntryMetadata (
95+ zipEntry ,
96+ lastModified ,
97+ UnixStat .FILE_FLAG | (contents .isExecutable () ? 0755 : 0644 )
98+ );
8599 zipOutputStream .putArchiveEntry (zipEntry );
86100 contents .write (zipOutputStream );
87101 zipOutputStream .closeArchiveEntry ();
@@ -92,4 +106,27 @@ public void close() throws IOException {
92106 zipOutputStream .finish ();
93107 zipOutputStream .close ();
94108 }
109+
110+ private void createParentDirs (String entryName , FileTime lastModified ) throws IOException {
111+ int slash = entryName .lastIndexOf ('/' );
112+ if (slash < 0 ) return ;
113+
114+ int i = 0 ;
115+ while ((i = entryName .indexOf ('/' , i )) >= 0 ) {
116+ String dir = entryName .substring (0 , i + 1 );
117+ if (createdDirs .add (dir )) {
118+ ZipArchiveEntry directoryEntry = new ZipArchiveEntry (dir );
119+ setZipEntryMetadata (directoryEntry , lastModified , UnixStat .DIR_FLAG | 0755 );
120+ zipOutputStream .putArchiveEntry (directoryEntry );
121+ zipOutputStream .closeArchiveEntry ();
122+ }
123+ i ++;
124+ }
125+ }
126+
127+ private void setZipEntryMetadata (ZipArchiveEntry zipEntry , FileTime lastModified , int unixMode ) {
128+ zipEntry .setLastModifiedTime (lastModified );
129+ zipEntry .setTime (lastModified .toMillis ());
130+ zipEntry .setUnixMode (unixMode );
131+ }
95132}
0 commit comments