Repository: GoogleContainerTools/jib
File: jib-core/src/main/java/com/google/cloud/tools/jib/image/ReproducibleLayerBuilder.java (Java)

Code before:
  }

  private final ImmutableList<FileEntry> layerEntries;

  public ReproducibleLayerBuilder(ImmutableList<FileEntry> layerEntries) {
    this.layerEntries = layerEntries;
  }

  /**
   * Builds and returns the layer {@link Blob}.
   *
   * @return the new layer
   */
  public Blob build() {
    UniqueTarArchiveEntries uniqueTarArchiveEntries = new UniqueTarArchiveEntries();

    // Adds all the layer entries as tar entries.
    for (FileEntry layerEntry : layerEntries) {
      // Adds the entries to uniqueTarArchiveEntries, which makes sure all entries are unique and
      // adds parent directories for each extraction path.
      TarArchiveEntry entry;
      try {
        entry = new TarArchiveEntry(
                layerEntry.getSourceFile(), layerEntry.getExtractionPath().toString());
      } catch (IOException e) {
        throw new UncheckedIOException(e);
      }

      // Sets the entry's permissions by masking out the permission bits from the entry's mode (the
      // lowest 9 bits) then using a bitwise OR to set them to the layerEntry's permissions.
      entry.setMode((entry.getMode() & ~0777) | layerEntry.getPermissions().getPermissionBits());
      entry.setModTime(layerEntry.getModificationTime().toEpochMilli());
      setUserAndGroup(entry, layerEntry);

      uniqueTarArchiveEntries.add(entry);
    }

    // Gets the entries sorted by extraction path.
    List<TarArchiveEntry> sortedFilesystemEntries = uniqueTarArchiveEntries.getSortedEntries();

    Set<String> names = new HashSet<>();

    // Adds all the files to a tar stream.
    TarStreamBuilder tarStreamBuilder = new TarStreamBuilder();
    for (TarArchiveEntry entry : sortedFilesystemEntries) {
      Verify.verify(!names.contains(entry.getName()));
      names.add(entry.getName());

      tarStreamBuilder.addTarArchiveEntry(entry);
    }


Review comment (suggestion):
Likewise, have `build() throws IOException`. All will be fine; no more propagated changes beyond this point.

Code after:
    this.layerEntries = layerEntries;
  }

  /**
   * Builds and returns the layer {@link Blob}.
   *
   * @return the new layer
   * @throws IOException if an I/O error occurs
   */
  public Blob build() throws IOException {
    UniqueTarArchiveEntries uniqueTarArchiveEntries = new UniqueTarArchiveEntries();

    // Adds all the layer entries as tar entries.
    for (FileEntry layerEntry : layerEntries) {
      // Adds the entries to uniqueTarArchiveEntries, which makes sure all entries are unique and
      // adds parent directories for each extraction path.
      TarArchiveEntry entry =
          new TarArchiveEntry(
              layerEntry.getSourceFile(), layerEntry.getExtractionPath().toString());

      // Sets the entry's permissions by masking out the permission bits from the entry's mode (the
      // lowest 9 bits) then using a bitwise OR to set them to the layerEntry's permissions.
      entry.setMode((entry.getMode() & ~0777) | layerEntry.getPermissions().getPermissionBits());
      entry.setModTime(layerEntry.getModificationTime().toEpochMilli());
      setUserAndGroup(entry, layerEntry);

      uniqueTarArchiveEntries.add(entry);
    }

    // Gets the entries sorted by extraction path.
    List<TarArchiveEntry> sortedFilesystemEntries = uniqueTarArchiveEntries.getSortedEntries();

    Set<String> names = new HashSet<>();

    // Adds all the files to a tar stream.
    TarStreamBuilder tarStreamBuilder = new TarStreamBuilder();
    for (TarArchiveEntry entry : sortedFilesystemEntries) {
      Verify.verify(!names.contains(entry.getName()));
      names.add(entry.getName());

      tarStreamBuilder.addTarArchiveEntry(entry);
    }

    return Blobs.from(tarStreamBuilder::writeAsTarArchiveTo);
  }
}