Skip to content

Fix: deleteSourceAfterCompress fails when used with tarBefore#45

Merged
insign merged 4 commits into
mainfrom
fix-sdel-tar-before-8491631351715671453
Apr 7, 2026
Merged

Fix: deleteSourceAfterCompress fails when used with tarBefore#45
insign merged 4 commits into
mainfrom
fix-sdel-tar-before-8491631351715671453

Conversation

@insign
Copy link
Copy Markdown
Contributor

@insign insign commented Apr 7, 2026

Este PR propaga corretamente a flag de exclusão (sdel / deleteSourceAfterCompress) para a instância intermediária ao utilizar a função tarBefore() (que também é ativada por formatos como tar.7z).

Por que isso agrega valor real ao projeto?
Esta mudança corrige um bug sério onde ->deleteSourceAfterCompress() falhava silenciosamente em deletar a fonte original (os arquivos ou pastas) quando a compressão utilizava tar como intermediário, deixando os arquivos originais ocupando espaço no disco do usuário de forma inesperada. Ao corrigir isso, garantimos a integridade do comportamento esperado pela flag e economizamos espaço para os utilizadores.

Arquivos Modificados

  • src/SevenZip.php: Atualizado executeTarBefore() para checar se a flag sdel está configurada através de array_key_exists (pois ela pode não ter valor atribuído e ser null) e aplicá-la à instância local ($sz), que agora cuidará de apagar a origem.
  • tests/SevenZipTest.php: Adicionado o teste abrangente testDeleteSourceAfterCompressWithTarBefore que comprova que as pastas raízes são propriamente removidas.
  • Nenhuma documentação precisou ser alterada, pois este é estritamente um conserto de bug de um recurso já documentado (a sua funcionalidade agora operará como o esperado).

PR created automatically by Jules for task 8491631351715671453 started by @insign

When using formats that implicitly require `tarBefore()` (such as `tar.7z`) or explicitly calling `tarBefore()`, the `sdel` flag (Delete source files after compression) was not being propagated to the intermediate SevenZip instance responsible for generating the initial `.tar` archive.

Because of this, the final step would only delete the intermediate `.tar` archive but leave the original source directories and files intact, completely breaking the expected user experience of `->deleteSourceAfterCompress()`.

This commit propagates the `sdel` flag to the inner SevenZip process properly.

A PHPUnit test `testDeleteSourceAfterCompressWithTarBefore` has been added to prevent regressions.

Co-authored-by: insign <1113045+insign@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b93dedf893

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/SevenZip.php Outdated
Comment on lines +1592 to +1594
if (array_key_exists('sdel', $this->getCustomFlags())) {
$sz->deleteSourceAfterCompress();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Delete source only after final tar-before stage succeeds

Propagating sdel to the intermediate TAR command deletes the original source before the outer compression finishes. In the tar.* flow, executeTarBefore() runs first, and the second compression runs later in compress(). If that second step fails (for example, permission error or disk full on the final target), the original source is already gone, causing irreversible data loss despite the archive not being produced. Source deletion should happen only after the full two-step compression succeeds.

Useful? React with 👍 / 👎.

@insign
Copy link
Copy Markdown
Contributor Author

insign commented Apr 7, 2026

Blocking: this change propagates into the intermediate TAR creation step inside . That deletes the original source before the outer compression step has succeeded. If the final archive creation fails afterwards (for example target write failure, disk full, permission issue), the user loses the original files even though the requested archive was not produced. There is also a concrete CI failure on this PR: Static Analysis is red because the PHPStan baseline was changed in the PR description/intent but not actually updated in the branch. This is not safe to merge as-is.

@google-labs-jules
Copy link
Copy Markdown
Contributor

Blocking: this change propagates into the intermediate TAR creation step inside . That deletes the original source before the outer compression step has succeeded. If the final archive creation fails afterwards (for example target write failure, disk full, permission issue), the user loses the original files even though the requested archive was not produced. There is also a concrete CI failure on this PR: Static Analysis is red because the PHPStan baseline was changed in the PR description/intent but not actually updated in the branch. This is not safe to merge as-is.

You are absolutely right. My previous approach was incorrectly propagating sdel to the intermediate tar process, which deleted the original source before the final .7z outer archive succeeded. Also, the PHPStan implicitlyNullable errors were not actually fixed in the code before I updated the baseline, breaking the CI on PHP 8.4.

I have rewritten the sdel handling: executeTarBefore() now correctly does not receive the sdel propagation for the inner tar. Instead, we capture the wasAlreadyTarred state before the outer runCommand() executes, and if it was set (and sdel was originally requested), we manually delete the original source only after the final compression finishes successfully.

I also properly fixed the PHPStan deprecations by explicitly typing the default null arguments as nullable (?) and removed the ignores from the baseline. Pre-commit passes cleanly now.

Thank you for catching these!

When using formats that implicitly require `tarBefore()` (such as `tar.7z`) or explicitly calling `tarBefore()`, the `sdel` flag (Delete source files after compression) was not properly deleting the original source directory or file.

This commit updates `compress()` to manually delete the original source directory or file only *after* the final `.7z` outer compression finishes successfully. This prevents data loss in case the final target write fails. It leverages `str_ends_with` to handle wildcard source patterns appropriately.

Additionally, this commit resolves the PHP 8.4 implicitlyNullable deprecation warnings in `SevenZip.php` and `ExecutableNotFoundException.php` and correctly updates the `phpstan-baseline.neon`.

Co-authored-by: insign <1113045+insign@users.noreply.github.com>
@insign
Copy link
Copy Markdown
Contributor Author

insign commented Apr 7, 2026

Still blocking: this replaces 7zs own sdel behavior with a custom recursive deleter in the library. That changes semantics and introduces a new destructive path that is not covered by the current test. In particular, deleteFileOrDirectory() follows PHP file/dir checks instead of archive-tool semantics, so sources like symlinks or non-trivial glob patterns can be handled differently from normal sdel and may delete the wrong thing or fail silently. Please either keep deletion delegated to 7z with a safer flow, or add coverage that proves the manual deletion path is correct for the supported source shapes.

@google-labs-jules
Copy link
Copy Markdown
Contributor

Still blocking: this replaces 7zs own sdel behavior with a custom recursive deleter in the library. That changes semantics and introduces a new destructive path that is not covered by the current test. In particular, deleteFileOrDirectory() follows PHP file/dir checks instead of archive-tool semantics, so sources like symlinks or non-trivial glob patterns can be handled differently from normal sdel and may delete the wrong thing or fail silently. Please either keep deletion delegated to 7z with a safer flow, or add coverage that proves the manual deletion path is correct for the supported source shapes.

You bring up a crucial point about semantic parity with sdel.

I have significantly improved the PHP deleteFileOrDirectory mechanism to correctly and safely handle glob patterns (using PHP's glob()) and symlinks (using is_link and ensuring we unlink() them without following).

Additionally, I added extensive tests (testDeleteSourceAfterCompressWithTarBeforeAndGlob and testDeleteSourceAfterCompressWithTarBeforeAndSymlink) to explicitly prove that our manual fallback matches 7-Zip's sdel expectations: symlinks are removed without modifying their targets, and glob patterns process files accurately.

…cases

When using formats that implicitly require `tarBefore()` (such as `tar.7z`) or explicitly calling `tarBefore()`, the `sdel` flag (Delete source files after compression) was not properly deleting the original source directory or file.

This commit updates `compress()` to manually delete the original source directory or file only *after* the final `.7z` outer compression finishes successfully, preventing data loss.

To ensure safety and match 7z's internal `sdel` behaviors, the `deleteFileOrDirectory` recursive deletion method was upgraded to safely process glob patterns and explicitly `unlink()` symlinks without destructively following them into target directories.

Tests were added specifically covering standard paths, globs (`*`), and symlink behavior.

Additionally, this commit resolves the PHP 8.4 implicitlyNullable deprecation warnings in `SevenZip.php` and `ExecutableNotFoundException.php` and correctly updates the `phpstan-baseline.neon`.

Co-authored-by: insign <1113045+insign@users.noreply.github.com>
@insign
Copy link
Copy Markdown
Contributor Author

insign commented Apr 7, 2026

Still blocking: even with the added glob and symlink coverage, the library is still performing source deletion outside the 7z command and outside its success/error model. That changes behavior in an important way: if the final manual cleanup only partially fails due to permissions, concurrent file changes, or rmdir/unlink warnings, compress() can still return a successful archive result while silently leaving an inconsistent source tree behind. Native sdel ties deletion to the archive tool execution path; this fallback does not. I do not consider this safe to merge unless cleanup failure is handled as a first-class outcome with explicit error propagation and coverage for partial-delete failure scenarios.

@google-labs-jules
Copy link
Copy Markdown
Contributor

Still blocking: even with the added glob and symlink coverage, the library is still performing source deletion outside the 7z command and outside its success/error model. That changes behavior in an important way: if the final manual cleanup only partially fails due to permissions, concurrent file changes, or rmdir/unlink warnings, compress() can still return a successful archive result while silently leaving an inconsistent source tree behind. Native sdel ties deletion to the archive tool execution path; this fallback does not. I do not consider this safe to merge unless cleanup failure is handled as a first-class outcome with explicit error propagation and coverage for partial-delete failure scenarios.

You make an excellent point regarding the risk of a partial cleanup silently leaving an inconsistent state.

I have updated the custom deleteFileOrDirectory fallback to handle deletion failures as first-class outcomes. By utilizing !@unlink and !@rmdir, the cleanup process will now immediately throw a \RuntimeException if an underlying permission issue or concurrent modification prevents deletion.

This exception is bubbled up explicitly inside compress(), ensuring the user is immediately aware of any incomplete deletion. Finally, I implemented testDeleteSourceAfterCompressWithTarBeforePartialFailure, which uses directory permissions (chmod 0555) to simulate this precise edge case and validates that the library surfaces the RuntimeException properly.

When using formats that implicitly require `tarBefore()` (such as `tar.7z`) or explicitly calling `tarBefore()`, the `sdel` flag (Delete source files after compression) was not properly deleting the original source directory or file.

This commit updates `compress()` to manually delete the original source directory or file only *after* the final `.7z` outer compression finishes successfully, preventing data loss.

To ensure safety and match 7z's internal `sdel` expectations, the `deleteFileOrDirectory` recursive deletion method was upgraded to safely process glob patterns, explicitly `unlink()` symlinks without following them, and most importantly, bubble up exceptions when a partial deletion failure occurs (due to permissions, locks, etc.), preserving consistent behavioral state.

Tests were added specifically covering standard paths, globs (`*`), symlink behavior, and partial deletion failure handling.

Additionally, this commit resolves the PHP 8.4 implicitlyNullable deprecation warnings in `SevenZip.php` and `ExecutableNotFoundException.php` and correctly updates the `phpstan-baseline.neon`.

Co-authored-by: insign <1113045+insign@users.noreply.github.com>
@insign insign merged commit b4fc599 into main Apr 7, 2026
9 checks passed
@insign insign deleted the fix-sdel-tar-before-8491631351715671453 branch April 7, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant