Skip to content

Commit 33284df

Browse files
feat(ir): add typed helper for ExtractFiles@1 (#1042)
Adds `extract_files_step()` factory function to `src/compile/ir/tasks.rs`. Required inputs are positional parameters (`archiveFilePatterns`, `destinationFolder`); optional inputs (`cleanDestinationFolder`, `overwriteExistingFiles`, `pathToSevenZipTool`) are applied via `.with_input(…)` on the returned `TaskStep`. Four unit tests cover: required-input defaults, optional clean/overwrite flags, custom 7z path, and multiline archive patterns. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9479a31 commit 33284df

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

src/compile/ir/tasks.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,39 @@ pub fn archive_files_step(
122122
.with_input("archiveFile", archive_file)
123123
}
124124

125+
/// Returns a [`TaskStep`] for `ExtractFiles@1`.
126+
///
127+
/// Extracts archives matching `archive_file_patterns` into `destination_folder`.
128+
/// Supports `.zip`, `.tar.gz`, `.tar.bz2`, and 7-Zip formats (`.7z`, `.tar`,
129+
/// `.rar`, etc.) via the 7z utility bundled with the task on Windows agents,
130+
/// or the system 7z on Linux/macOS.
131+
///
132+
/// - `archive_file_patterns` — glob pattern(s) that match the archives to
133+
/// extract. Patterns are evaluated from the root of the repository
134+
/// (equivalent to `$(Build.SourcesDirectory)`). Multiple patterns can be
135+
/// separated by newlines. Default: `**/*.zip`.
136+
/// - `destination_folder` — path to the folder where files will be extracted.
137+
/// **Required** — the task has no default for this input.
138+
///
139+
/// Optional inputs (applied with `.with_input(…)` on the returned value):
140+
///
141+
/// | Input key | Type | Default | Description |
142+
/// |---|---|---|---|
143+
/// | `cleanDestinationFolder` | bool string | `"true"` | Delete destination folder contents before extracting. |
144+
/// | `overwriteExistingFiles` | bool string | `"false"` | Overwrite files that already exist in the destination. |
145+
/// | `pathToSevenZipTool` | string | — | Absolute path to a custom `7z` binary (e.g. `/usr/local/bin/7z`). |
146+
///
147+
/// ADO task reference:
148+
/// <https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/extract-files-v1>
149+
pub fn extract_files_step(
150+
archive_file_patterns: impl Into<String>,
151+
destination_folder: impl Into<String>,
152+
) -> TaskStep {
153+
TaskStep::new("ExtractFiles@1", "Extract Files")
154+
.with_input("archiveFilePatterns", archive_file_patterns)
155+
.with_input("destinationFolder", destination_folder)
156+
}
157+
125158
/// Returns a [`TaskStep`] for `PublishTestResults@2`.
126159
///
127160
/// Publishes test results to the ADO build summary and timeline.
@@ -446,4 +479,62 @@ mod tests {
446479
);
447480
assert_eq!(t.inputs.len(), 4);
448481
}
482+
483+
// ── ExtractFiles@1 ───────────────────────────────────────────────────
484+
485+
#[test]
486+
fn extract_files_step_sets_task_and_required_inputs() {
487+
let t = extract_files_step("**/*.zip", "$(Build.BinariesDirectory)");
488+
assert_eq!(t.task, "ExtractFiles@1");
489+
assert_eq!(t.display_name, "Extract Files");
490+
assert_eq!(
491+
t.inputs.get("archiveFilePatterns").map(|s| s.as_str()),
492+
Some("**/*.zip")
493+
);
494+
assert_eq!(
495+
t.inputs.get("destinationFolder").map(|s| s.as_str()),
496+
Some("$(Build.BinariesDirectory)")
497+
);
498+
// no optional inputs by default
499+
assert_eq!(t.inputs.len(), 2);
500+
}
501+
502+
#[test]
503+
fn extract_files_step_accepts_optional_clean_and_overwrite() {
504+
let t = extract_files_step("**/*.tar.gz", "$(Agent.TempDirectory)/extracted")
505+
.with_input("cleanDestinationFolder", "false")
506+
.with_input("overwriteExistingFiles", "true");
507+
assert_eq!(t.task, "ExtractFiles@1");
508+
assert_eq!(
509+
t.inputs.get("cleanDestinationFolder").map(|s| s.as_str()),
510+
Some("false")
511+
);
512+
assert_eq!(
513+
t.inputs.get("overwriteExistingFiles").map(|s| s.as_str()),
514+
Some("true")
515+
);
516+
assert_eq!(t.inputs.len(), 4);
517+
}
518+
519+
#[test]
520+
fn extract_files_step_accepts_custom_seven_zip_path() {
521+
let t = extract_files_step("artifacts/**/*.7z", "$(Build.BinariesDirectory)")
522+
.with_input("pathToSevenZipTool", "/usr/local/bin/7z");
523+
assert_eq!(t.task, "ExtractFiles@1");
524+
assert_eq!(
525+
t.inputs.get("pathToSevenZipTool").map(|s| s.as_str()),
526+
Some("/usr/local/bin/7z")
527+
);
528+
assert_eq!(t.inputs.len(), 3);
529+
}
530+
531+
#[test]
532+
fn extract_files_step_multiline_patterns() {
533+
let patterns = "**/*.zip\n**/*.tar.gz";
534+
let t = extract_files_step(patterns, "$(Build.BinariesDirectory)");
535+
assert_eq!(
536+
t.inputs.get("archiveFilePatterns").map(|s| s.as_str()),
537+
Some("**/*.zip\n**/*.tar.gz")
538+
);
539+
}
449540
}

0 commit comments

Comments
 (0)