Skip to content

Commit bce6846

Browse files
committed
�[>4;2m展開時に、同名ディレクトリが 入れ子になる状態を回避。
1 parent 7ca834c commit bce6846

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

PROMPT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@
2828
25. Java Version のデフォルトを 21 にしたい
2929
26. Base Dir を削除し、 Artifact ID と同じ値を使う用に修正したい
3030
27. Output Zip を削除し、 Artifact ID に .zip を付けた名前で出力するようにしたい
31+
28. 展開時に、同名ディレクトリが 入れ子になる状態を回避したいですができますか?

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ TUI の操作(tview ベース)
4747
- `--base-dir` : 展開時のプロジェクトルート名(未指定は `artifact-id`
4848
- `--output` : ZIP の保存先ファイル名(デフォルト: `<artifact-id>.zip`
4949
- `--extract` : ZIP をダウンロード後に展開
50+
- アーカイブ内に単一のトップレベルディレクトリがあり、その名前が `--base-dir`(デフォルトは `artifact-id`)と同一の場合は、そのトップレベルを自動的に取り除いて展開します(`<base-dir>/<base-dir>/...` の二重ネストを回避)。
5051
- `--dry-run` : 作成される URL を表示して終了(ダウンロードはしない)
5152
- `--base-url` : Spring Initializr のベース URL(デフォルト: `https://start.spring.io`
5253
- `-v` : 冗長ログ

fs.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io"
66
"os"
77
"path/filepath"
8+
"strings"
89
)
910

1011
// saveToFile writes the reader to the given file path, creating directories as needed.
@@ -33,8 +34,45 @@ func unzip(zipPath, destDir string) error {
3334
return err
3435
}
3536

37+
// Detect if the zip contains a single top-level directory that matches
38+
// the destination base directory name. If so, strip that component to
39+
// avoid nested same-name directories like destDir/destDir/...
40+
base := filepath.Base(destDir)
41+
topLevels := make(map[string]struct{})
3642
for _, f := range zr.File {
37-
p := filepath.Join(destDir, f.Name)
43+
// Normalize separators in zip entries
44+
name := strings.TrimLeft(strings.ReplaceAll(f.Name, "\\", "/"), "/")
45+
if name == "" {
46+
continue
47+
}
48+
// Extract first path component
49+
if i := strings.IndexByte(name, '/'); i >= 0 {
50+
topLevels[name[:i]] = struct{}{}
51+
} else {
52+
topLevels[name] = struct{}{}
53+
}
54+
}
55+
56+
var stripPrefix string
57+
if len(topLevels) == 1 {
58+
for tl := range topLevels {
59+
if tl == base {
60+
stripPrefix = tl + "/"
61+
}
62+
}
63+
}
64+
65+
for _, f := range zr.File {
66+
// Normalize and optionally strip the top-level prefix
67+
name := strings.TrimLeft(strings.ReplaceAll(f.Name, "\\", "/"), "/")
68+
if stripPrefix != "" {
69+
name = strings.TrimPrefix(name, stripPrefix)
70+
}
71+
if name == "" {
72+
// nothing to create (e.g., top-level dir entry when stripped)
73+
continue
74+
}
75+
p := filepath.Join(destDir, name)
3876
if f.FileInfo().IsDir() {
3977
if err := os.MkdirAll(p, f.Mode()); err != nil {
4078
return err
@@ -63,4 +101,3 @@ func unzip(zipPath, destDir string) error {
63101
}
64102
return nil
65103
}
66-

0 commit comments

Comments
 (0)