From 0d63e2773e11e8fb29769c5756e805994c1da02f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:34:56 +0000 Subject: [PATCH 01/12] Initial plan From 9139445ef0089289cc6ef754d5ce892907da6bca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:38:23 +0000 Subject: [PATCH 02/12] Implement STDIN support for wp media import command Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/media-import.feature | 36 +++++++++++++ src/Media_Command.php | 96 +++++++++++++++++++++++++---------- 2 files changed, 106 insertions(+), 26 deletions(-) diff --git a/features/media-import.feature b/features/media-import.feature index aa8562fc..15f3dfbe 100644 --- a/features/media-import.feature +++ b/features/media-import.feature @@ -286,3 +286,39 @@ Feature: Manage WordPress attachments """ Error: Invalid value for : invalid. Expected flag or 'url'. """ + + Scenario: Import media from STDIN + Given download: + | path | url | + | {CACHE_DIR}/codeispoetry.png | http://wp-cli.org/behat-data/codeispoetry.png | + + When I run `cat {CACHE_DIR}/codeispoetry.png | wp media import - --title="From STDIN" --porcelain` + Then save STDOUT as {ATTACHMENT_ID} + + When I run `wp post get {ATTACHMENT_ID} --field=title` + Then STDOUT should be: + """ + From STDIN + """ + + Scenario: Import media from STDIN with file_name + Given download: + | path | url | + | {CACHE_DIR}/codeispoetry.png | http://wp-cli.org/behat-data/codeispoetry.png | + + When I run `cat {CACHE_DIR}/codeispoetry.png | wp media import - --file_name=my-image.png --porcelain` + Then save STDOUT as {ATTACHMENT_ID} + + When I run `wp post get {ATTACHMENT_ID} --field=name` + Then STDOUT should be: + """ + my-image-png + """ + + Scenario: Fail to import from STDIN when no input provided + When I try `wp media import - ] * : ID of the post to attach the imported files to. @@ -261,6 +262,11 @@ public function regenerate( $args, $assoc_args = array() ) { * # Get the URL for an attachment after import. * $ wp media import http://s.wordpress.org/style/images/wp-header-logo.png --porcelain | xargs -I {} wp post list --post__in={} --field=url --post_type=attachment * http://wordpress-develop.dev/wp-header-logo/ + * + * # Import an image from STDIN. + * $ curl http://example.com/image.jpg | wp media import - --title="From STDIN" + * Imported file from STDIN as attachment ID 1756. + * Success: Imported 1 of 1 items. */ public function import( $args, $assoc_args = array() ) { $assoc_args = wp_parse_args( @@ -309,41 +315,79 @@ public function import( $args, $assoc_args = array() ) { Utils\wp_clear_object_cache(); } - // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url -- parse_url will only be used in absence of wp_parse_url. - $is_file_remote = function_exists( 'wp_parse_url' ) ? wp_parse_url( $file, PHP_URL_HOST ) : parse_url( $file, PHP_URL_HOST ); - $orig_filename = $file; - $file_time = ''; + // Handle STDIN input + if ( '-' === $file ) { + if ( ! Utils\has_stdin() ) { + WP_CLI::warning( 'Unable to import file from STDIN. Reason: No input provided.' ); + ++$errors; + continue; + } - if ( empty( $is_file_remote ) ) { - if ( ! file_exists( $file ) ) { - WP_CLI::warning( "Unable to import file '$file'. Reason: File doesn't exist." ); + // Read from STDIN and save to a temporary file + $stdin_content = file_get_contents( 'php://stdin' ); + if ( false === $stdin_content ) { + WP_CLI::warning( 'Unable to import file from STDIN. Reason: Could not read STDIN.' ); ++$errors; continue; } - if ( Utils\get_flag_value( $assoc_args, 'skip-copy' ) ) { - $tempfile = $file; - } else { - $tempfile = $this->make_copy( $file ); + + // Create a temporary file to store STDIN content + $tempfile = wp_tempnam( 'wp-media-import-' ); + if ( false === file_put_contents( $tempfile, $stdin_content ) ) { + WP_CLI::warning( 'Unable to import file from STDIN. Reason: Could not write to temporary file.' ); + ++$errors; + continue; } - $name = Utils\basename( $file ); - if ( Utils\get_flag_value( $assoc_args, 'preserve-filetime' ) ) { - $file_time = @filemtime( $file ); + // Determine the name for the imported file + if ( ! empty( $assoc_args['file_name'] ) ) { + $name = $assoc_args['file_name']; + } else { + // Try to determine file extension from content + $filetype = wp_check_filetype_and_ext( $tempfile, '' ); + $ext = ! empty( $filetype['ext'] ) ? '.' . $filetype['ext'] : ''; + $name = 'stdin-' . time() . $ext; } + + $orig_filename = 'STDIN'; + $file_time = ''; } else { - $tempfile = download_url( $file ); - if ( is_wp_error( $tempfile ) ) { - WP_CLI::warning( - sprintf( - "Unable to import file '%s'. Reason: %s", - $file, - implode( ', ', $tempfile->get_error_messages() ) - ) - ); - ++$errors; - continue; + // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url -- parse_url will only be used in absence of wp_parse_url. + $is_file_remote = function_exists( 'wp_parse_url' ) ? wp_parse_url( $file, PHP_URL_HOST ) : parse_url( $file, PHP_URL_HOST ); + $orig_filename = $file; + $file_time = ''; + + if ( empty( $is_file_remote ) ) { + if ( ! file_exists( $file ) ) { + WP_CLI::warning( "Unable to import file '$file'. Reason: File doesn't exist." ); + ++$errors; + continue; + } + if ( Utils\get_flag_value( $assoc_args, 'skip-copy' ) ) { + $tempfile = $file; + } else { + $tempfile = $this->make_copy( $file ); + } + $name = Utils\basename( $file ); + + if ( Utils\get_flag_value( $assoc_args, 'preserve-filetime' ) ) { + $file_time = @filemtime( $file ); + } + } else { + $tempfile = download_url( $file ); + if ( is_wp_error( $tempfile ) ) { + WP_CLI::warning( + sprintf( + "Unable to import file '%s'. Reason: %s", + $file, + implode( ', ', $tempfile->get_error_messages() ) + ) + ); + ++$errors; + continue; + } + $name = strtok( Utils\basename( $file ), '?' ); } - $name = strtok( Utils\basename( $file ), '?' ); } if ( ! empty( $assoc_args['file_name'] ) ) { From 09ecad92250448ba44b45cdfe10b810e76c63698 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:39:41 +0000 Subject: [PATCH 03/12] Simplify STDIN file naming logic and update class documentation Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/Media_Command.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Media_Command.php b/src/Media_Command.php index b04b9b75..8390f0e9 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -20,6 +20,11 @@ * Imported file '/home/person/Downloads/image.png' as attachment ID 1753 and attached to post 123 as featured image. * Success: Imported 1 of 1 images. * + * # Import an image from STDIN. + * $ curl http://example.com/image.jpg | wp media import - + * Imported file 'STDIN' as attachment ID 1754. + * Success: Imported 1 of 1 items. + * * # List all registered image sizes * $ wp media image-size * +---------------------------+-------+--------+-------+ @@ -339,15 +344,10 @@ public function import( $args, $assoc_args = array() ) { continue; } - // Determine the name for the imported file - if ( ! empty( $assoc_args['file_name'] ) ) { - $name = $assoc_args['file_name']; - } else { - // Try to determine file extension from content - $filetype = wp_check_filetype_and_ext( $tempfile, '' ); - $ext = ! empty( $filetype['ext'] ) ? '.' . $filetype['ext'] : ''; - $name = 'stdin-' . time() . $ext; - } + // Try to determine file extension from content + $filetype = wp_check_filetype_and_ext( $tempfile, '' ); + $ext = ! empty( $filetype['ext'] ) ? '.' . $filetype['ext'] : ''; + $name = 'stdin-' . time() . $ext; $orig_filename = 'STDIN'; $file_time = ''; From 9ef41067693fe303a03dec041226578c0a54f381 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 21:40:53 +0000 Subject: [PATCH 04/12] Add clarifying comment about WordPress slug sanitization in test Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/media-import.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/media-import.feature b/features/media-import.feature index 15f3dfbe..9ef2032f 100644 --- a/features/media-import.feature +++ b/features/media-import.feature @@ -314,6 +314,7 @@ Feature: Manage WordPress attachments """ my-image-png """ + # Note: WordPress sanitizes the post_name (slug) by converting dots to hyphens Scenario: Fail to import from STDIN when no input provided When I try `wp media import - Date: Sun, 15 Feb 2026 16:53:35 +0000 Subject: [PATCH 05/12] Fix STDIN file type detection using mime_content_type Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- src/Media_Command.php | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Media_Command.php b/src/Media_Command.php index 8390f0e9..744eb4e3 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -330,8 +330,8 @@ public function import( $args, $assoc_args = array() ) { // Read from STDIN and save to a temporary file $stdin_content = file_get_contents( 'php://stdin' ); - if ( false === $stdin_content ) { - WP_CLI::warning( 'Unable to import file from STDIN. Reason: Could not read STDIN.' ); + if ( false === $stdin_content || empty( $stdin_content ) ) { + WP_CLI::warning( 'Unable to import file from STDIN. Reason: No input provided.' ); ++$errors; continue; } @@ -344,10 +344,24 @@ public function import( $args, $assoc_args = array() ) { continue; } - // Try to determine file extension from content - $filetype = wp_check_filetype_and_ext( $tempfile, '' ); - $ext = ! empty( $filetype['ext'] ) ? '.' . $filetype['ext'] : ''; - $name = 'stdin-' . time() . $ext; + // Determine file extension from content + $mimetype = mime_content_type( $tempfile ); + + // Map MIME type to extension + $ext = ''; + if ( $mimetype && function_exists( 'wp_get_mime_types' ) ) { + $mime_types = wp_get_mime_types(); + foreach ( $mime_types as $exts => $mime ) { + if ( $mime === $mimetype ) { + $ext_array = explode( '|', $exts ); + $ext = '.' . $ext_array[0]; + break; + } + } + } + + // Generate filename with proper extension + $name = 'stdin-' . time() . $ext; $orig_filename = 'STDIN'; $file_time = ''; From 4ee243484af94f672e7a6a89b14fdf87b4cad4d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:56:36 +0000 Subject: [PATCH 06/12] Fix test to use correct field name post_name instead of name Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Agent-Logs-Url: https://github.com/wp-cli/media-command/sessions/4e4b402c-3f76-450c-abcd-1f37785681e8 --- features/media-import.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/media-import.feature b/features/media-import.feature index d695d2e8..cf5af946 100644 --- a/features/media-import.feature +++ b/features/media-import.feature @@ -309,7 +309,7 @@ Feature: Manage WordPress attachments When I run `cat {CACHE_DIR}/codeispoetry.png | wp media import - --file_name=my-image.png --porcelain` Then save STDOUT as {ATTACHMENT_ID} - When I run `wp post get {ATTACHMENT_ID} --field=name` + When I run `wp post get {ATTACHMENT_ID} --field=post_name` Then STDOUT should be: """ my-image-png From 5ca73d8c9e9a63f4694ce52a5c41cfe414993a29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:21:54 +0000 Subject: [PATCH 07/12] Fix STDIN imports to use generated filename for title instead of '-' Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Agent-Logs-Url: https://github.com/wp-cli/media-command/sessions/0f687dc2-b2b2-4363-8524-c9f12cb1338a --- src/Media_Command.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Media_Command.php b/src/Media_Command.php index 640e6e5c..ad513a7c 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -652,7 +652,9 @@ public function import( $args, $assoc_args = array() ) { } if ( empty( $post_array['post_title'] ) ) { - $post_array['post_title'] = preg_replace( '/\.[^.]+$/', '', Path::basename( $file ) ); + // For STDIN imports, use the generated filename instead of the '-' argument + $title_source = ( '-' === $file ) ? $name : $file; + $post_array['post_title'] = preg_replace( '/\.[^.]+$/', '', Path::basename( $title_source ) ); } if ( Utils\get_flag_value( $assoc_args, 'skip-copy' ) ) { From 18743cdc02e7ec9f536ff7ed10fe15666d002def Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 21 Mar 2026 08:01:09 +0100 Subject: [PATCH 08/12] Update src/Media_Command.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Media_Command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Media_Command.php b/src/Media_Command.php index ad513a7c..ae4edcab 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -461,7 +461,7 @@ public function prune( $args, $assoc_args = array() ) { * * # Import an image from STDIN. * $ curl http://example.com/image.jpg | wp media import - --title="From STDIN" - * Imported file from STDIN as attachment ID 1756. + * Imported file 'STDIN' as attachment ID 1756. * Success: Imported 1 of 1 items. * * @param string[] $args Positional arguments. From 5675e4c6efbd994c3a724eb32cd327e5afcf9024 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 21 Mar 2026 08:01:37 +0100 Subject: [PATCH 09/12] Update features/media-import.feature Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- features/media-import.feature | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/features/media-import.feature b/features/media-import.feature index cf5af946..b18326fb 100644 --- a/features/media-import.feature +++ b/features/media-import.feature @@ -316,6 +316,15 @@ Feature: Manage WordPress attachments """ # Note: WordPress sanitizes the post_name (slug) by converting dots to hyphens + When I run `wp post meta get {ATTACHMENT_ID} _wp_attached_file` + Then STDOUT should contain: + """ + my-image.png + """ + And STDOUT should not contain: + """ + my-image.png.png + """ Scenario: Fail to import from STDIN when no input provided When I try `wp media import - Date: Sat, 21 Mar 2026 07:12:47 +0000 Subject: [PATCH 10/12] Fix double extension issue by stripping extension from file_name before appending Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Agent-Logs-Url: https://github.com/wp-cli/media-command/sessions/25fc9db3-37e7-41b5-aef7-c41b4b633a8c --- src/Media_Command.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Media_Command.php b/src/Media_Command.php index ae4edcab..eb1c243d 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -2065,7 +2065,10 @@ private function get_image_name( $basename, $slug ) { $extension = pathinfo( $basename, PATHINFO_EXTENSION ); - return $slug . '.' . $extension; + // Strip any extension from the slug to prevent double extensions + $slug_without_ext = preg_replace( '/\.[^.]+$/', '', $slug ); + + return $slug_without_ext . '.' . $extension; } /** From 36a77d8e019cf700bb3840bc364b0e107f7b7968 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 07:15:21 +0000 Subject: [PATCH 11/12] Stream STDIN directly to temp file to avoid memory issues with large files Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Agent-Logs-Url: https://github.com/wp-cli/media-command/sessions/2d71c7b3-1844-42e8-ab88-e67d3b17e044 --- src/Media_Command.php | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Media_Command.php b/src/Media_Command.php index eb1c243d..07edc531 100644 --- a/src/Media_Command.php +++ b/src/Media_Command.php @@ -534,21 +534,42 @@ public function import( $args, $assoc_args = array() ) { } // Read from STDIN and save to a temporary file - $stdin_content = file_get_contents( 'php://stdin' ); - if ( false === $stdin_content || empty( $stdin_content ) ) { - WP_CLI::warning( 'Unable to import file from STDIN. Reason: No input provided.' ); + // Stream STDIN directly to temp file to avoid memory issues with large files + $stdin_handle = fopen( 'php://stdin', 'rb' ); + if ( false === $stdin_handle ) { + WP_CLI::warning( 'Unable to import file from STDIN. Reason: Could not open STDIN.' ); ++$errors; continue; } // Create a temporary file to store STDIN content $tempfile = wp_tempnam( 'wp-media-import-' ); - if ( false === file_put_contents( $tempfile, $stdin_content ) ) { + if ( false === $tempfile ) { + fclose( $stdin_handle ); + WP_CLI::warning( 'Unable to import file from STDIN. Reason: Could not create temporary file.' ); + ++$errors; + continue; + } + + $temp_handle = fopen( $tempfile, 'wb' ); + if ( false === $temp_handle ) { + fclose( $stdin_handle ); WP_CLI::warning( 'Unable to import file from STDIN. Reason: Could not write to temporary file.' ); ++$errors; continue; } + // Stream data from STDIN to temp file + $bytes_copied = stream_copy_to_stream( $stdin_handle, $temp_handle ); + fclose( $stdin_handle ); + fclose( $temp_handle ); + + if ( false === $bytes_copied || 0 === $bytes_copied ) { + WP_CLI::warning( 'Unable to import file from STDIN. Reason: No input provided.' ); + ++$errors; + continue; + } + // Determine file extension from content $mimetype = mime_content_type( $tempfile ); From 9bc5229342c4ea26727b7cc64f14ff7174c1d832 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:12:52 +0000 Subject: [PATCH 12/12] Update test expectation for post_name after fixing double extension bug Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Agent-Logs-Url: https://github.com/wp-cli/media-command/sessions/125cfbc3-1470-4b02-98d1-cadf88177d9c --- features/media-import.feature | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/features/media-import.feature b/features/media-import.feature index b18326fb..1a8ced91 100644 --- a/features/media-import.feature +++ b/features/media-import.feature @@ -312,9 +312,8 @@ Feature: Manage WordPress attachments When I run `wp post get {ATTACHMENT_ID} --field=post_name` Then STDOUT should be: """ - my-image-png + my-image """ - # Note: WordPress sanitizes the post_name (slug) by converting dots to hyphens When I run `wp post meta get {ATTACHMENT_ID} _wp_attached_file` Then STDOUT should contain: