Visitar URL original
Feat add recursive directory support to push by TerryHowe · Pull Request #1951 · oras-project/oras · GitHub
Skip to content

Feat add recursive directory support to push - #1951

Open
TerryHowe wants to merge 6 commits into
oras-project:mainfrom
TerryHowe:feat-add-recursive-directory-support-to-push
Open

TerryHowe wants to merge 6 commits into
oras-project:mainfrom
TerryHowe:feat-add-recursive-directory-support-to-push

Conversation

@TerryHowe

@TerryHowe TerryHowe commented Dec 28, 2025 •

Copy link
Copy Markdown
Member

What this PR does / why we need it:

Recursive directory copy for oras push

Fixes: #1871

@TerryHowe
TerryHowe marked this pull request as draft December 28, 2025 17:36
@TerryHowe TerryHowe mentioned this pull request Dec 28, 2025
1 task done
@codecov

codecov Bot commented Dec 28, 2025 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.52632% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.86%. Comparing base (b50ff65) to head (c5bd0c1).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
cmd/oras/root/push.go 71.42% 14 Missing and 14 partials ⚠️
internal/manifestutil/builder.go 95.62% 3 Missing and 3 partials ⚠️
internal/dir/walker.go 98.30% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1951      +/-   ##
==========================================
+ Coverage   61.90%   63.86%   +1.96%     
==========================================
  Files         140      144       +4     
  Lines        6100     6479     +379     
==========================================
+ Hits         3776     4138     +362     
+ Misses       2101     2100       -1     
- Partials      223      241      +18     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@TerryHowe
TerryHowe force-pushed the feat-add-recursive-directory-support-to-push branch 2 times, most recently from ed59da2 to 1ade71a Compare December 28, 2025 17:56
@lcarva

lcarva commented Jan 9, 2026

Copy link
Copy Markdown

Thanks for implementing this!

I'm having success with oras push --recursive. However, I'm getting a failures with oras pull.

Generate some file data:

mkdir -p sample/dirs-only/{assets,config}
mkdir -p sample/files-only
mkdir -p sample/mixed/{src,tests}

fortune > sample/dirs-only/assets/logo.png
fortune > sample/dirs-only/config/settings.json
fortune > sample/files-only/config.txt
fortune > sample/files-only/data.json
fortune > sample/files-only/readme.md
fortune > sample/mixed/package.json
fortune > sample/mixed/README.md
fortune > sample/mixed/src/main.js
fortune > sample/mixed/tests/test.js

That should create the following:

$ tree sample
sample
├── dirs-only
│   ├── assets
│   │   └── logo.png
│   └── config
│       └── settings.json
├── files-only
│   ├── config.txt
│   ├── data.json
│   └── readme.md
└── mixed
    ├── package.json
    ├── README.md
    ├── src
    │   └── main.js
    └── tests
        └── test.js

This is successful:

$ oras push --recursive localhost:5000/oras/sample:latest ./sample

On a different directory, this fails:

$ oras pull localhost:5000/oras/sample:latest
✓ Pulled      files-only/config.txt                                                                    61/61  B 100.00%   83µs
  └─ sha256:989523c961939b4ab17482eea045896c679dd5695498df91fcb1941c607371df                                                  
✓ Pulled      mixed/src/main.js                                                                      164/164  B 100.00%   93µs
  └─ sha256:2f37125c73db3133437db82348cace40c4532a4e24f880237d12fa98d5d2968d                                                  
✓ Pulled      files-only/data.json                                                                     25/25  B 100.00%  195µs
  └─ sha256:6e95b87f32d8b6ba1f82e1dd8e25731f986f4ab58b92d05d3e6c9466dbd223b7                                                  
✓ Pulled      mixed/README.md                                                                        228/228  B 100.00%   87µs
  └─ sha256:4025c62d58896ee69189054e7527a9521cde1d94f184719a6fd44a77380c73d7                                                  
✓ Pulled      mixed/package.json                                                                     140/140  B 100.00%   10µs
  └─ sha256:0fbac35a1bb9a5fe62859a5c4d673b0e9eb7e7a70f3da5aef7a09a5cd38b281a                                                  
✓ Pulled      mixed/tests/test.js                                                                    152/152  B 100.00%   54µs
  └─ sha256:6abc94092687a502683788c37db77f6a6471c268c3edef0917c5de64e58a77ba                                                  
✓ Pulled      files-only/readme.md                                                                   596/596  B 100.00%    5µs
  └─ sha256:b693e58040e276bc3f4bc3bb1307bacfa1db22c702c42e11a183f92311ca0b98                                                  
✓ Pulled      dirs-only/assets/logo.png                                                                54/54  B 100.00%   45µs
  └─ sha256:c4563c17afb4e4229a57ac01defb3c742c48844347d2eaa2fd9fc188c9b16269                                                  
✓ Pulled      src                                                                                    571/571  B 100.00%  122µs
  └─ sha256:9832ec6366b5e58602c09745eb3bb5d5b95ab512d3d3bbe19f8a28cfe992b05f                                                  
✓  mixed                                                                                             790/790  B 100.00%     0s
  └─ sha256:74e1558a5e2700f61983035b8bfa733185a588c92da1c997ef69deddb113b4bc                                                  
✓  files-only                                                                                      1022/1022  B 100.00%     0s
  └─ sha256:2ff8374f79f0e130eeb815c94628e3d3ff09ffa92d3026c66a941bff1c354164                                                  
Error: failed to create file /tmp/scratch/crazy-noether/mixed: open /tmp/scratch/crazy-noether/mixed: is a directory

I think the problem is related to the org.opencontainers.image.title annotation. Each sub-directory is getting recreated as an empty file at the root.

Comment thread cmd/oras/root/push.go Resolved
Comment thread internal/descriptor/annotations.go Resolved
Comment thread internal/dir/walker.go Resolved
Comment thread internal/dir/walker.go Resolved
Comment thread internal/dir/walker.go Resolved
@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions Bot added the stale Inactive issues or pull requests label Feb 24, 2026
@TerryHowe TerryHowe added keep open and removed stale Inactive issues or pull requests labels Feb 24, 2026
@TerryHowe TerryHowe added this to the v1.4.0 milestone Feb 24, 2026
robertream added a commit to robertream/oras that referenced this pull request May 1, 2026
…e push

When buildNode stored child Image Index descriptors in the parent index's
manifests list, it called maps.Copy to attach directory annotations — but
createIndex had already set ocispec.AnnotationTitle=<dirname> on the
returned descriptor, so maps.Copy preserved it.

oras pull treats any descriptor with AnnotationTitle as a named file and
calls metadataHandler.OnFilePulled, which tries to open/create the path.
When a directory with that name already exists on disk this fails with:
  "open <path>/<dirname>: is a directory"

Fix:
- createIndex no longer adds AnnotationTitle to its annotations or the
  returned descriptor
- buildNode assigns (replaces) directory annotations instead of merging,
  ensuring no stale AnnotationTitle can survive from the inner call

Adds TestBuildFromNode_noAnnotationTitleOnIndexDescriptors as a regression
test that reproduces the exact failure mode reported in PR oras-project#1951 review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
robertream added a commit to robertream/oras that referenced this pull request May 1, 2026
Adds three e2e specs under "pushing recursively with --recursive flag":

1. Push a nested directory tree and pull it back, verifying every file's
   content is preserved — this is the primary regression test for the
   "open <path>: is a directory" bug from PR oras-project#1951 review.

2. Error: --recursive with a file argument (not a directory).

3. Error: --recursive combined with --config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
robertream added a commit to robertream/oras that referenced this pull request May 6, 2026
…e push

When buildNode stored child Image Index descriptors in the parent index's
manifests list, it called maps.Copy to attach directory annotations — but
createIndex had already set ocispec.AnnotationTitle=<dirname> on the
returned descriptor, so maps.Copy preserved it.

oras pull treats any descriptor with AnnotationTitle as a named file and
calls metadataHandler.OnFilePulled, which tries to open/create the path.
When a directory with that name already exists on disk this fails with:
  "open <path>/<dirname>: is a directory"

Fix:
- createIndex no longer adds AnnotationTitle to its annotations or the
  returned descriptor
- buildNode assigns (replaces) directory annotations instead of merging,
  ensuring no stale AnnotationTitle can survive from the inner call

Adds TestBuildFromNode_noAnnotationTitleOnIndexDescriptors as a regression
test that reproduces the exact failure mode reported in PR oras-project#1951 review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>
robertream added a commit to robertream/oras that referenced this pull request May 6, 2026
Adds three e2e specs under "pushing recursively with --recursive flag":

1. Push a nested directory tree and pull it back, verifying every file's
   content is preserved — this is the primary regression test for the
   "open <path>: is a directory" bug from PR oras-project#1951 review.

2. Error: --recursive with a file argument (not a directory).

3. Error: --recursive combined with --config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>
robertream added a commit to robertream/oras that referenced this pull request May 6, 2026
…e push

When buildNode stored child Image Index descriptors in the parent index's
manifests list, it called maps.Copy to attach directory annotations — but
createIndex had already set ocispec.AnnotationTitle=<dirname> on the
returned descriptor, so maps.Copy preserved it.

oras pull treats any descriptor with AnnotationTitle as a named file and
calls metadataHandler.OnFilePulled, which tries to open/create the path.
When a directory with that name already exists on disk this fails with:
  "open <path>/<dirname>: is a directory"

Fix:
- createIndex no longer adds AnnotationTitle to its annotations or the
  returned descriptor
- buildNode assigns (replaces) directory annotations instead of merging,
  ensuring no stale AnnotationTitle can survive from the inner call

Adds TestBuildFromNode_noAnnotationTitleOnIndexDescriptors as a regression
test that reproduces the exact failure mode reported in PR oras-project#1951 review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>
robertream added a commit to robertream/oras that referenced this pull request May 6, 2026
Adds three e2e specs under "pushing recursively with --recursive flag":

1. Push a nested directory tree and pull it back, verifying every file's
   content is preserved — this is the primary regression test for the
   "open <path>: is a directory" bug from PR oras-project#1951 review.

2. Error: --recursive with a file argument (not a directory).

3. Error: --recursive combined with --config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>
@TerryHowe
TerryHowe force-pushed the feat-add-recursive-directory-support-to-push branch from d1199e1 to c98ffdd Compare September 28, 2026 17:53
@TerryHowe
TerryHowe marked this pull request as ready for review September 28, 2026 19:17
robertream added a commit to robertream/oras that referenced this pull request Oct 22, 2026
…e push

When buildNode stored child Image Index descriptors in the parent index's
manifests list, it called maps.Copy to attach directory annotations — but
createIndex had already set ocispec.AnnotationTitle=<dirname> on the
returned descriptor, so maps.Copy preserved it.

oras pull treats any descriptor with AnnotationTitle as a named file and
calls metadataHandler.OnFilePulled, which tries to open/create the path.
When a directory with that name already exists on disk this fails with:
  "open <path>/<dirname>: is a directory"

Fix:
- createIndex no longer adds AnnotationTitle to its annotations or the
  returned descriptor
- buildNode assigns (replaces) directory annotations instead of merging,
  ensuring no stale AnnotationTitle can survive from the inner call

Adds TestBuildFromNode_noAnnotationTitleOnIndexDescriptors as a regression
test that reproduces the exact failure mode reported in PR oras-project#1951 review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>
robertream added a commit to robertream/oras that referenced this pull request Oct 22, 2026
Adds three e2e specs under "pushing recursively with --recursive flag":

1. Push a nested directory tree and pull it back, verifying every file's
   content is preserved — this is the primary regression test for the
   "open <path>: is a directory" bug from PR oras-project#1951 review.

2. Error: --recursive with a file argument (not a directory).

3. Error: --recursive combined with --config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>
robertream added a commit to robertream/oras that referenced this pull request Oct 23, 2026
…e push

When buildNode stored child Image Index descriptors in the parent index's
manifests list, it called maps.Copy to attach directory annotations — but
createIndex had already set ocispec.AnnotationTitle=<dirname> on the
returned descriptor, so maps.Copy preserved it.

oras pull treats any descriptor with AnnotationTitle as a named file and
calls metadataHandler.OnFilePulled, which tries to open/create the path.
When a directory with that name already exists on disk this fails with:
  "open <path>/<dirname>: is a directory"

Fix:
- createIndex no longer adds AnnotationTitle to its annotations or the
  returned descriptor
- buildNode assigns (replaces) directory annotations instead of merging,
  ensuring no stale AnnotationTitle can survive from the inner call

Adds TestBuildFromNode_noAnnotationTitleOnIndexDescriptors as a regression
test that reproduces the exact failure mode reported in PR oras-project#1951 review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>

Signed-off-by: Robert Ream <robertream@gmail.com>
robertream added a commit to robertream/oras that referenced this pull request Oct 23, 2026
Adds three e2e specs under "pushing recursively with --recursive flag":

1. Push a nested directory tree and pull it back, verifying every file's
   content is preserved — this is the primary regression test for the
   "open <path>: is a directory" bug from PR oras-project#1951 review.

2. Error: --recursive with a file argument (not a directory).

3. Error: --recursive combined with --config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robert Ream <154010+robertream@users.noreply.github.com>

Signed-off-by: Robert Ream <robertream@gmail.com>
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
TerryHowe and others added 5 commits October 28, 2026 14:45
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
Add comprehensive unit tests for the recursive push feature to
improve test coverage of cmd/oras/root/push.go.

Signed-off-by: Terry Howe <terrylhowe@gmail.com>
Signed-off-by: Terry Howe <thowe@nvidia.com>
Signed-off-by: Terry Howe <thowe@nvidia.com>
@TerryHowe
TerryHowe force-pushed the feat-add-recursive-directory-support-to-push branch from 841c061 to c5bd0c1 Compare October 28, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add recursive directory support

2 participants