• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 17832256085

18 Sep 2025 02:38PM UTC coverage: 68.464% (-0.08%) from 68.539%
17832256085

Pull #1499

github

web-flow
Merge 0ad75342a into 147222dbe
Pull Request #1499: #907: add NpmRepository and further node/npm support as preparation for yarn and corepack

3430 of 5487 branches covered (62.51%)

Branch coverage included in aggregate %.

8977 of 12635 relevant lines covered (71.05%)

3.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.11
cli/src/main/java/com/devonfw/tools/ide/io/FileAccess.java
1
package com.devonfw.tools.ide.io;
2

3
import java.io.OutputStream;
4
import java.io.Reader;
5
import java.nio.file.Path;
6
import java.nio.file.StandardCopyOption;
7
import java.nio.file.attribute.PosixFilePermission;
8
import java.time.Duration;
9
import java.util.List;
10
import java.util.Properties;
11
import java.util.Set;
12
import java.util.function.Consumer;
13
import java.util.function.Function;
14
import java.util.function.Predicate;
15

16
import com.devonfw.tools.ide.io.ini.IniFile;
17
import com.devonfw.tools.ide.io.ini.IniFileImpl;
18

19
/**
20
 * Interface that gives access to various operations on files.
21
 */
22
public interface FileAccess {
23

24
  /** {@link PosixFilePermission}s for "rwxr-xr-x" or 0755. */
25
  Set<PosixFilePermission> RWX_RX_RX = Set.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE,
10✔
26
      PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE);
27

28
  /**
29
   * Downloads a file from an arbitrary location.
30
   *
31
   * @param url the location of the binary file to download. May also be a local or remote path to copy from.
32
   * @param targetFile the {@link Path} to the target file to download to. Should not already exist. Missing parent directories will be created
33
   *     automatically.
34
   */
35
  void download(String url, Path targetFile);
36

37
  /**
38
   * @param url the URL of the text to download.
39
   * @return the downloaded body as {@link String} (e.g. JSON or XML).
40
   */
41
  String download(String url);
42

43
  /**
44
   * Creates the entire {@link Path} as directories if not already existing.
45
   *
46
   * @param directory the {@link Path} to {@link java.nio.file.Files#createDirectories(Path, java.nio.file.attribute.FileAttribute...) create}.
47
   */
48
  void mkdirs(Path directory);
49

50
  /**
51
   * @param file the {@link Path} to check.
52
   * @return {@code true} if the given {@code file} points to an existing file, {@code false} otherwise (the given {@link Path} does not exist or is a
53
   *     directory).
54
   */
55
  boolean isFile(Path file);
56

57
  /**
58
   * @param folder the {@link Path} to check.
59
   * @return {@code true} if the given {@code folder} points to an existing directory, {@code false} otherwise (a warning is logged in this case).
60
   */
61
  boolean isExpectedFolder(Path folder);
62

63
  /**
64
   * @param file the {@link Path} to compute the checksum of.
65
   * @param hashAlgorithm the hash algorithm (e.g. SHA-266).
66
   * @return the computed hash checksum as hex {@link String}.
67
   */
68
  String checksum(Path file, String hashAlgorithm);
69

70
  /**
71
   * Moves the given {@link Path} to the backup.
72
   *
73
   * @param fileOrFolder the {@link Path} to move to the backup (soft-deletion).
74
   * @return the {@link Path} in the backup where the given {@link Path} was moved to.
75
   */
76
  Path backup(Path fileOrFolder);
77

78
  /**
79
   * @param source the source {@link Path file or folder} to move.
80
   * @param targetDir the {@link Path} with the directory to move {@code source} into.
81
   * @param copyOptions the {@link java.nio.file.CopyOption} which specify how the move should be done
82
   */
83
  void move(Path source, Path targetDir, StandardCopyOption... copyOptions);
84

85
  /**
86
   * Creates a relative symbolic link. If the given {@code targetLink} already exists and is a symbolic link or a Windows junction, it will be replaced. In case
87
   * of missing privileges, Windows Junctions may be used as fallback, which must point to absolute paths. Therefore, the created link will be absolute instead
88
   * of relative.
89
   *
90
   * @param source the source {@link Path} to link to, may be relative or absolute.
91
   * @param targetLink the {@link Path} where the symbolic link shall be created pointing to {@code source}.
92
   */
93
  default void symlink(Path source, Path targetLink) {
94

95
    symlink(source, targetLink, true);
5✔
96
  }
1✔
97

98
  /**
99
   * Creates a {@link PathLinkType#SYMBOLIC_LINK symbolic link}. If the given {@code link} already exists and is a symbolic link or a Windows junction, it will
100
   * be replaced. In case of missing privileges, Windows mklink may be used as fallback, which must point to absolute paths. In such case the {@code relative}
101
   * flag will be ignored.
102
   *
103
   * @param target the target {@link Path} to link to, may be relative or absolute.
104
   * @param link the {@link Path} where the symbolic link shall be created pointing to {@code target}.
105
   * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute.
106
   */
107
  default void symlink(Path target, Path link, boolean relative) {
108

109
    link(target, link, relative, PathLinkType.SYMBOLIC_LINK);
6✔
110
  }
1✔
111

112
  /**
113
   * Creates a {@link PathLinkType#HARD_LINK hard link}. If the given {@code link} already exists and is a symbolic link or a Windows junction, it will be
114
   * replaced. In case of missing privileges, Windows mklink may be used as fallback.
115
   *
116
   * @param target the target {@link Path} to link to, may be relative or absolute.
117
   * @param link the {@link Path} where the symbolic link shall be created pointing to {@code target}.
118
   */
119
  default void hardlink(Path target, Path link) {
120

121
    link(target, link, false, PathLinkType.HARD_LINK);
×
122
  }
×
123

124
  /**
125
   * Creates a link. If the given {@code link} already exists and is a symbolic link or a Windows junction, it will be replaced. In case of missing privileges,
126
   * Windows mklink may be used as fallback, which must point to absolute paths. In such case the {@code relative} flag will be ignored.
127
   *
128
   * @param target the target {@link Path} to link to, may be relative or absolute.
129
   * @param link the {@link Path} where the symbolic link shall be created pointing to {@code target}.
130
   * @param relative - {@code true} if the symbolic link shall be relative, {@code false} if it shall be absolute.
131
   * @param type the {@link PathLinkType}.
132
   */
133
  void link(Path target, Path link, boolean relative, PathLinkType type);
134

135
  /**
136
   * @param link the {@link PathLink} to {@link #link(Path, Path, boolean, PathLinkType) create}.
137
   */
138
  default void link(PathLink link) {
139
    link(link.target(), link.link(), true, link.type());
×
140
  }
×
141

142
  /**
143
   * @param source the source {@link Path file or folder} to copy.
144
   * @param target the {@link Path} to copy {@code source} to. See {@link #copy(Path, Path, FileCopyMode)} for details. Will always ensure that in the end
145
   *     you will find the same content of {@code source} in {@code target}.
146
   */
147
  default void copy(Path source, Path target) {
148

149
    copy(source, target, FileCopyMode.COPY_TREE_FAIL_IF_EXISTS);
5✔
150
  }
1✔
151

152
  /**
153
   * @param source the source {@link Path file or folder} to copy.
154
   * @param target the {@link Path} to copy {@code source} to. Unlike the Linux {@code cp} command this method will not take the filename of {@code source}
155
   *     and copy that to {@code target} in case that is an existing folder. Instead it will always be simple and stupid and just copy from {@code source} to
156
   *     {@code target}. Therefore the result is always clear and easy to predict and understand. Also you can easily rename a file to copy. While
157
   *     {@code cp my-file target} may lead to a different result than {@code cp my-file target/} this method will always ensure that in the end you will find
158
   *     the same content of {@code source} in {@code target}.
159
   * @param mode the {@link FileCopyMode}.
160
   */
161
  default void copy(Path source, Path target, FileCopyMode mode) {
162

163
    copy(source, target, mode, PathCopyListener.NONE);
6✔
164
  }
1✔
165

166
  /**
167
   * @param source the source {@link Path file or folder} to copy.
168
   * @param target the {@link Path} to copy {@code source} to. Unlike the Linux {@code cp} command this method will not take the filename of {@code source}
169
   *     and copy that to {@code target} in case that is an existing folder. Instead it will always be simple and stupid and just copy from {@code source} to
170
   *     {@code target}. Therefore the result is always clear and easy to predict and understand. Also you can easily rename a file to copy. While
171
   *     {@code cp my-file target} may lead to a different result than {@code cp my-file target/} this method will always ensure that in the end you will find
172
   *     the same content of {@code source} in {@code target}.
173
   * @param mode the {@link FileCopyMode}.
174
   * @param listener the {@link PathCopyListener} that will be called for each copied {@link Path}.
175
   */
176
  void copy(Path source, Path target, FileCopyMode mode, PathCopyListener listener);
177

178
  /**
179
   * @param archiveFile the {@link Path} to the file to extract.
180
   * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile} to.
181
   */
182
  default void extract(Path archiveFile, Path targetDir) {
183

184
    extract(archiveFile, targetDir, null);
5✔
185
  }
1✔
186

187
  /**
188
   * @param archiveFile the {@link Path} to the archive file to extract.
189
   * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}.
190
   * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is moved to {@code targetDir}.
191
   */
192
  default void extract(Path archiveFile, Path targetDir, Consumer<Path> postExtractHook) {
193

194
    extract(archiveFile, targetDir, postExtractHook, true);
6✔
195
  }
1✔
196

197
  /**
198
   * @param archiveFile the {@link Path} to the archive file to extract.
199
   * @param targetDir the {@link Path} to the directory where to extract the {@code archiveFile}.
200
   * @param postExtractHook the {@link Consumer} to be called after the extraction on the final folder before it is moved to {@code targetDir}.
201
   * @param extract {@code true} if the {@code archiveFile} should be extracted (default), {@code false} otherwise.
202
   */
203
  void extract(Path archiveFile, Path targetDir, Consumer<Path> postExtractHook, boolean extract);
204

205
  /**
206
   * Extracts a ZIP file what is the common archive format on Windows. Initially invented by PKZIP for MS-DOS and also famous from WinZIP software for Windows.
207
   *
208
   * @param file the ZIP file to extract.
209
   * @param targetDir the {@link Path} with the directory to unzip to.
210
   */
211
  void extractZip(Path file, Path targetDir);
212

213
  /**
214
   * @param file the ZIP file to extract.
215
   * @param targetDir the {@link Path} with the directory to unzip to.
216
   * @param compression the {@link TarCompression} to use.
217
   */
218
  void extractTar(Path file, Path targetDir, TarCompression compression);
219

220
  /**
221
   * @param file the JAR file to extract.
222
   * @param targetDir the {@link Path} with the directory to extract to.
223
   */
224
  void extractJar(Path file, Path targetDir);
225

226
  /**
227
   * Extracts an Apple DMG (Disk Image) file that is similar to an ISO image. DMG files are commonly used for software releases on MacOS. Double-clicking such
228
   * files on MacOS mounts them and show the application together with a symbolic link to the central applications folder and some help instructions. The user
229
   * then copies the application to the applications folder via drag and drop in order to perform the installation.
230
   *
231
   * @param file the DMG file to extract.
232
   * @param targetDir the target directory where to extract the contents to.
233
   */
234
  void extractDmg(Path file, Path targetDir);
235

236
  /**
237
   * Extracts an MSI (Microsoft Installer) file. MSI files are commonly used for software releases on Windows that allow an installation wizard and easy later
238
   * uninstallation.
239
   *
240
   * @param file the MSI file to extract.
241
   * @param targetDir the target directory where to extract the contents to.
242
   */
243
  void extractMsi(Path file, Path targetDir);
244

245
  /**
246
   * Extracts an Apple PKG (Package) file. PKG files are used instead of {@link #extractDmg(Path, Path) DMG files} if additional changes have to be performed
247
   * like drivers to be installed. Similar to what {@link #extractMsi(Path, Path) MSI} is on Windows. PKG files are internally a xar based archive with a
248
   * specific structure.
249
   *
250
   * @param file the PKG file to extract.
251
   * @param targetDir the target directory where to extract the contents to.
252
   */
253
  void extractPkg(Path file, Path targetDir);
254

255
  /**
256
   * @param dir the {@link Path directory} to compress.
257
   * @param out the {@link OutputStream} to write the compressed data to.
258
   * @param format the path, filename or extension to derive the archive format from (e.g. "tgz", "tar.gz", "zip", etc.).
259
   */
260
  void compress(Path dir, OutputStream out, String format);
261

262
  /**
263
   * @param dir the {@link Path directory} to compress as TAR with given {@link TarCompression}.
264
   * @param out the {@link OutputStream} to write the compressed data to.
265
   * @param tarCompression the {@link TarCompression} to use for the TAR archive.
266
   */
267
  void compressTar(Path dir, OutputStream out, TarCompression tarCompression);
268

269
  /**
270
   * @param dir the {@link Path directory} to compress as TAR.
271
   * @param out the {@link OutputStream} to write the compressed data to.
272
   */
273
  void compressTar(Path dir, OutputStream out);
274

275
  /**
276
   * @param dir the {@link Path directory} to compress as TGZ.
277
   * @param out the {@link OutputStream} to write the compressed data to.
278
   */
279
  void compressTarGz(Path dir, OutputStream out);
280

281
  /**
282
   * @param dir the {@link Path directory} to compress as TBZ2.
283
   * @param out the {@link OutputStream} to write the compressed data to.
284
   */
285
  void compressTarBzip2(Path dir, OutputStream out);
286

287
  /**
288
   * @param dir the {@link Path directory} to compress as ZIP.
289
   * @param out the {@link OutputStream} to write the compressed data to.
290
   */
291
  void compressZip(Path dir, OutputStream out);
292

293
  /**
294
   * @param path the {@link Path} to convert.
295
   * @return the absolute and physical {@link Path} (without symbolic links).
296
   */
297
  Path toRealPath(Path path);
298

299
  /**
300
   * @param path the {@link Path} to convert.
301
   * @return the absolute and physical {@link Path}.
302
   */
303
  Path toCanonicalPath(Path path);
304

305
  /**
306
   * Deletes the given {@link Path} idempotent and recursive.
307
   * <p>
308
   * ATTENTION: In most cases we want to use {@link #backup(Path)} instead to prevent the user from data loss.
309
   * </p>
310
   *
311
   * @param path the {@link Path} to delete.
312
   */
313
  void delete(Path path);
314

315
  /**
316
   * Creates a new temporary directory. ATTENTION: The user of this method is responsible to do house-keeping and {@link #delete(Path) delete} it after the work
317
   * is done.
318
   *
319
   * @param name the default name of the temporary directory to create. A prefix or suffix may be added to ensure uniqueness.
320
   * @return the {@link Path} to the newly created and unique temporary directory.
321
   */
322
  Path createTempDir(String name);
323

324
  /**
325
   * @param dir the folder to search.
326
   * @param filter the {@link Predicate} used to find the {@link Predicate#test(Object) match}.
327
   * @param recursive - {@code true} to search recursive in all sub-folders, {@code false} otherwise.
328
   * @return the first child {@link Path} matching the given {@link Predicate} or {@code null} if no match was found.
329
   */
330
  Path findFirst(Path dir, Predicate<Path> filter, boolean recursive);
331

332
  /**
333
   * @param dir the {@link Path} to the directory where to list the children.
334
   * @param filter the {@link Predicate} used to {@link Predicate#test(Object) decide} which children to include (if {@code true} is returned).
335
   * @return all children of the given {@link Path} that match the given {@link Predicate}. Will be the empty list of the given {@link Path} is not an existing
336
   *     directory.
337
   */
338
  default List<Path> listChildren(Path dir, Predicate<Path> filter) {
339

340
    return listChildrenMapped(dir, child -> (filter.test(child)) ? child : null);
13!
341
  }
342

343
  /**
344
   * @param dir the {@link Path} to the directory where to list the children.
345
   * @param filter the filter {@link Function} used to {@link Function#apply(Object) filter and transform} children to include. If the {@link Function}
346
   *     returns  {@code null}, the child will be filtered, otherwise the returned {@link Path} will be included in the resulting {@link List}.
347
   * @return all children of the given {@link Path} returned by the given {@link Function}. Will be the empty list if the given {@link Path} is not an existing
348
   *     directory.
349
   */
350
  List<Path> listChildrenMapped(Path dir, Function<Path, Path> filter);
351

352
  /**
353
   * Finds the existing file with the specified name in the given list of directories.
354
   *
355
   * @param fileName The name of the file to find.
356
   * @param searchDirs The list of directories to search for the file.
357
   * @return The {@code Path} of the existing file, or {@code null} if the file is not found.
358
   */
359
  Path findExistingFile(String fileName, List<Path> searchDirs);
360

361
  /**
362
   * Checks if the given directory is empty.
363
   *
364
   * @param dir The {@link Path} object representing the directory to check.
365
   * @return {@code true} if the directory is empty, {@code false} otherwise.
366
   */
367
  boolean isEmptyDir(Path dir);
368

369
  /**
370
   * Sets or unsets the writable permission for the specified file path.
371
   *
372
   * @param file {@link Path} to the file.
373
   * @param writable {@code true} to make the file writable, {@code false} to make it read-only
374
   * @return {@code true} if the operation was successful or supported, {@code false} otherwise
375
   */
376
  boolean setWritable(Path file, boolean writable);
377

378
  /**
379
   * Makes a path executable (analog to 'chmod a+x').
380
   *
381
   * @param path the {@link Path} to the file or directory.
382
   */
383
  default void makeExecutable(Path path) {
384

385
    makeExecutable(path, false);
4✔
386
  }
1✔
387

388
  /**
389
   * Makes a path executable (analog to 'chmod a+x').
390
   *
391
   * @param path the {@link Path} to the file or directory.
392
   * @param confirm - {@code true} to get user confirmation before adding missing executable flags, {@code false} otherwise (always set missing flags).
393
   */
394
  void makeExecutable(Path path, boolean confirm);
395

396
  /**
397
   * Sets the given {@link PathPermissions} for the specified {@link Path}.
398
   *
399
   * @param path the {@link Path} to the file or directory.
400
   * @param permissions the {@link PathPermissions} to set.
401
   * @param logErrorAndContinue - {@code true} to only log errors and continue, {@code false} to fail with an exception on error.
402
   */
403
  void setFilePermissions(Path path, PathPermissions permissions, boolean logErrorAndContinue);
404

405
  /**
406
   * Like the linux touch command this method will update the modification time of the given {@link Path} to the current
407
   * {@link System#currentTimeMillis() system time}. In case the file does not exist, it will be created as empty file. If already the
408
   * {@link Path#getParent() parent folder} does not exist, the operation will fail.
409
   *
410
   * @param file the {@link Path} to the file or folder.
411
   */
412
  void touch(Path file);
413

414
  /**
415
   * @param file the {@link Path} to the file to read.
416
   * @return the content of the specified file (in UTF-8 encoding), or {@code null} if the file doesn't exist.
417
   * @see java.nio.file.Files#readString(Path)
418
   */
419
  String readFileContent(Path file);
420

421
  /**
422
   * @param content the {@link String} with the text to write to a file.
423
   * @param file the {@link Path} to the file where to save.
424
   */
425
  default void writeFileContent(String content, Path file) {
426

427
    writeFileContent(content, file, false);
5✔
428
  }
1✔
429

430
  /**
431
   * @param content the {@link String} with the text to write to a file.
432
   * @param file the {@link Path} to the file where to save.
433
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
434
   *     not exist).
435
   */
436
  void writeFileContent(String content, Path file, boolean createParentDir);
437

438
  /**
439
   * Like {@link #readFileContent(Path)} but giving one {@link String} per line of text. It will not allow to preserve line endings (CRLF vs. LF).
440
   *
441
   * @param file the {@link Path} to the file to read.
442
   * @return the content of the specified file (in UTF-8 encoding) as {@link List} of {@link String}s per line of text.
443
   */
444
  List<String> readFileLines(Path file);
445

446
  /**
447
   * Like {@link #writeFileContent(String, Path)} but taking a {@link List} with one {@link String} per line of text. It will always use LF as newline character
448
   * independent of the operating system.
449
   *
450
   * @param lines the {@link List} of {@link String}s per line of text.
451
   * @param file the {@link Path} to the file where to save.
452
   */
453
  default void writeFileLines(List<String> lines, Path file) {
454
    writeFileLines(lines, file, false);
5✔
455
  }
1✔
456

457
  /**
458
   * Like {@link #writeFileContent(String, Path, boolean)} but taking a {@link List} with one {@link String} per line of text. It will always use LF as newline
459
   * character independent of the operating system.
460
   *
461
   * @param lines the {@link List} of {@link String}s per line of text.
462
   * @param file the {@link Path} to the file where to save.
463
   * @param createParentDir if {@code true}, the parent directory will be created if it does not already exist, {@code false} otherwise (fail if parent does
464
   *     not exist).
465
   */
466
  void writeFileLines(List<String> lines, Path file, boolean createParentDir);
467

468
  /**
469
   * @param path the {@link Path} to check.
470
   * @return {@code true} if the given {@link Path} is a junction, false otherwise.
471
   */
472
  boolean isJunction(Path path);
473

474
  /**
475
   * @param file the {@link Path} to the {@link Properties} file to read.
476
   * @return the parsed {@link Properties}.
477
   */
478
  default Properties readProperties(Path file) {
479
    Properties properties = new Properties();
4✔
480
    readProperties(file, properties);
4✔
481
    return properties;
2✔
482
  }
483

484
  /**
485
   * @param file the {@link Path} to the {@link Properties} file to read.
486
   * @param properties the existing {@link Properties} to {@link Properties#load(Reader) load} into.
487
   */
488
  void readProperties(Path file, Properties properties);
489

490
  /**
491
   * @param properties the {@link Properties} to save.
492
   * @param file the {@link Path} to the file where to save the properties.
493
   */
494
  default void writeProperties(Properties properties, Path file) {
495

496
    writeProperties(properties, file, false);
5✔
497
  }
1✔
498

499

500
  /**
501
   * @param properties the {@link Properties} to save.
502
   * @param file the {@link Path} to the file where to save the properties.
503
   * @param createParentDir if {@code true}, the parent directory will created if it does not already exist, {@code false} otherwise (fail if parent does
504
   *     not exist).
505
   */
506
  void writeProperties(Properties properties, Path file, boolean createParentDir);
507

508
  /**
509
   * @param file the {@link Path} to read from
510
   * @return {@link com.devonfw.tools.ide.io.ini.IniFile}
511
   */
512
  default IniFile readIniFile(Path file) {
513
    IniFile iniFile = new IniFileImpl();
4✔
514
    readIniFile(file, iniFile);
4✔
515
    return iniFile;
2✔
516
  }
517

518
  /**
519
   * @param file the {@link Path} to read from
520
   * @param iniFile the {@link IniFile} object the data is loaded into
521
   */
522
  void readIniFile(Path file, IniFile iniFile);
523

524
  /**
525
   * @param iniFile the {@link IniFile} object
526
   * @param file the {@link Path} to write to
527
   */
528
  default void writeIniFile(IniFile iniFile, Path file) {
529
    writeIniFile(iniFile, file, false);
5✔
530
  }
1✔
531

532
  /**
533
   * @param iniFile the {@link IniFile} object
534
   * @param file the {@link Path} to write to
535
   * @param createParentDir whether to create missing parent directories
536
   */
537
  void writeIniFile(IniFile iniFile, Path file, boolean createParentDir);
538

539
  /**
540
   * @param path the {@link Path} to get the age from the modification time.
541
   * @return the age of the file as {@link Duration} from now to the modification time of the file.
542
   */
543
  public Duration getFileAge(Path path);
544

545
  /**
546
   * @param path the {@link Path} to check.
547
   * @param cacheDuration the {@link Duration} to consider as recent.
548
   * @return {@code true} if the given {@link Path} exists and is recent enough (its {@link #getFileAge(Path) age} is not greater than the given
549
   *     {@link Duration}), {@code false} otherwise.
550
   */
551
  boolean isFileAgeRecent(Path path, Duration cacheDuration);
552
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc