Add a -i flag to force showing 'index.html' in the output listing; by default do not show them.

This commit is contained in:
Pim van Pelt
2025-12-03 12:23:40 +01:00
parent 7829000c55
commit 16fa899b91
4 changed files with 42 additions and 29 deletions

View File

@@ -125,6 +125,11 @@ func processS3Bucket(s3Config *S3Config, opts *indexgen.Options) error {
continue continue
} }
// Skip index.html files unless ShowIndexFiles is enabled
if !opts.ShowIndexFiles && strings.HasSuffix(keyName, opts.OutputFile) {
continue
}
// Simple glob matching for filter // Simple glob matching for filter
if opts.Filter != "*" && opts.Filter != "" { if opts.Filter != "*" && opts.Filter != "" {
matched, err := filepath.Match(opts.Filter, keyName) matched, err := filepath.Match(opts.Filter, keyName)
@@ -152,14 +157,14 @@ func processS3Bucket(s3Config *S3Config, opts *indexgen.Options) error {
func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.Client, s3Config *S3Config) error { func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.Client, s3Config *S3Config) error {
// Group objects by directory path // Group objects by directory path
dirMap := make(map[string][]indexgen.FileEntry) dirMap := make(map[string][]indexgen.FileEntry)
// Track all directory paths we need to create indexes for // Track all directory paths we need to create indexes for
allDirs := make(map[string]bool) allDirs := make(map[string]bool)
for _, obj := range objects { for _, obj := range objects {
// Split the key into directory parts // Split the key into directory parts
parts := strings.Split(obj.Key, "/") parts := strings.Split(obj.Key, "/")
if len(parts) == 1 { if len(parts) == 1 {
// Root level file // Root level file
entry := createFileEntry(obj, obj.Key) entry := createFileEntry(obj, obj.Key)
@@ -168,11 +173,11 @@ func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.C
// File in a subdirectory // File in a subdirectory
fileName := parts[len(parts)-1] fileName := parts[len(parts)-1]
dirPath := strings.Join(parts[:len(parts)-1], "/") dirPath := strings.Join(parts[:len(parts)-1], "/")
// Create file entry // Create file entry
entry := createFileEntry(obj, fileName) entry := createFileEntry(obj, fileName)
dirMap[dirPath] = append(dirMap[dirPath], entry) dirMap[dirPath] = append(dirMap[dirPath], entry)
// Track all parent directories // Track all parent directories
currentPath := "" currentPath := ""
for i, part := range parts[:len(parts)-1] { for i, part := range parts[:len(parts)-1] {
@@ -185,7 +190,7 @@ func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.C
} }
} }
} }
// Add directory entries to parent directories // Add directory entries to parent directories
for dirPath := range allDirs { for dirPath := range allDirs {
parentPath := "" parentPath := ""
@@ -193,14 +198,14 @@ func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.C
parts := strings.Split(dirPath, "/") parts := strings.Split(dirPath, "/")
parentPath = strings.Join(parts[:len(parts)-1], "/") parentPath = strings.Join(parts[:len(parts)-1], "/")
} }
dirName := filepath.Base(dirPath) dirName := filepath.Base(dirPath)
// Build the correct path for S3 // Build the correct path for S3
dirEntryPath := dirPath + "/" dirEntryPath := dirPath + "/"
if opts.DirAppend { if opts.DirAppend {
dirEntryPath += opts.OutputFile dirEntryPath += opts.OutputFile
} }
dirEntry := indexgen.FileEntry{ dirEntry := indexgen.FileEntry{
Name: dirName, Name: dirName,
Path: dirEntryPath, Path: dirEntryPath,
@@ -213,13 +218,13 @@ func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.C
ModTimeISO: time.Now().Format(time.RFC3339), ModTimeISO: time.Now().Format(time.RFC3339),
ModTimeHuman: time.Now().Format(time.RFC822), ModTimeHuman: time.Now().Format(time.RFC822),
} }
dirMap[parentPath] = append(dirMap[parentPath], dirEntry) dirMap[parentPath] = append(dirMap[parentPath], dirEntry)
} }
// Set TopDir to bucket name for template generation // Set TopDir to bucket name for template generation
opts.TopDir = s3Config.Bucket opts.TopDir = s3Config.Bucket
// Generate index.html for each directory // Generate index.html for each directory
for dirPath, entries := range dirMap { for dirPath, entries := range dirMap {
indexKey := dirPath indexKey := dirPath
@@ -227,13 +232,13 @@ func processS3Hierarchy(objects []S3Object, opts *indexgen.Options, client *s3.C
indexKey += "/" indexKey += "/"
} }
indexKey += opts.OutputFile indexKey += opts.OutputFile
err := generateS3HTML(entries, opts, client, s3Config, indexKey) err := generateS3HTML(entries, opts, client, s3Config, indexKey)
if err != nil { if err != nil {
return fmt.Errorf("failed to generate index for %s: %w", dirPath, err) return fmt.Errorf("failed to generate index for %s: %w", dirPath, err)
} }
} }
return nil return nil
} }
@@ -271,7 +276,7 @@ func generateS3HTML(entries []indexgen.FileEntry, opts *indexgen.Options, client
// Determine if we're at root level (no parent directory) // Determine if we're at root level (no parent directory)
isRoot := (indexKey == opts.OutputFile) // root level index.html isRoot := (indexKey == opts.OutputFile) // root level index.html
// Prepare template data (similar to ProcessDir in indexgen) // Prepare template data (similar to ProcessDir in indexgen)
data := struct { data := struct {
DirName string DirName string
@@ -293,7 +298,7 @@ func generateS3HTML(entries []indexgen.FileEntry, opts *indexgen.Options, client
if err != nil { if err != nil {
return fmt.Errorf("failed to execute template: %w", err) return fmt.Errorf("failed to execute template: %w", err)
} }
htmlContent := htmlBuffer.String() htmlContent := htmlBuffer.String()
if opts.DryRun { if opts.DryRun {
@@ -339,6 +344,7 @@ func main() {
var directory string var directory string
var s3URL string var s3URL string
var dryRun bool var dryRun bool
var showIndexFiles bool
// Set defaults // Set defaults
opts.DirAppend = true opts.DirAppend = true
@@ -352,6 +358,7 @@ func main() {
flag.BoolVar(&dryRun, "n", false, "dry run: show what would be written without actually writing") flag.BoolVar(&dryRun, "n", false, "dry run: show what would be written without actually writing")
flag.StringVar(&excludeRegexStr, "x", "", "exclude files matching regular expression") flag.StringVar(&excludeRegexStr, "x", "", "exclude files matching regular expression")
flag.BoolVar(&opts.Verbose, "v", false, "verbosely list every processed file") flag.BoolVar(&opts.Verbose, "v", false, "verbosely list every processed file")
flag.BoolVar(&showIndexFiles, "i", false, "show index.html files in directory listings")
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Generate directory index files (recursive is ON, hidden files included by default).\n") fmt.Fprintf(os.Stderr, "Generate directory index files (recursive is ON, hidden files included by default).\n")
@@ -391,8 +398,9 @@ func main() {
} }
} }
// Set dry run flag // Set dry run and show index files flags
opts.DryRun = dryRun opts.DryRun = dryRun
opts.ShowIndexFiles = showIndexFiles
if s3URL != "" { if s3URL != "" {
// Parse S3 URL // Parse S3 URL

View File

@@ -158,15 +158,16 @@ var ExtensionTypes = map[string]string{
} }
type Options struct { type Options struct {
TopDir string TopDir string
Filter string Filter string
OutputFile string OutputFile string
DirAppend bool DirAppend bool
Recursive bool Recursive bool
IncludeHidden bool IncludeHidden bool
ExcludeRegex *regexp.Regexp ExcludeRegex *regexp.Regexp
Verbose bool Verbose bool
DryRun bool DryRun bool
ShowIndexFiles bool
} }
type FileEntry struct { type FileEntry struct {
@@ -275,7 +276,7 @@ func ReadDirEntries(dirPath string, opts *Options) ([]FileEntry, error) {
for _, file := range files { for _, file := range files {
fileName := file.Name() fileName := file.Name()
if strings.EqualFold(fileName, opts.OutputFile) { if !opts.ShowIndexFiles && strings.EqualFold(fileName, opts.OutputFile) {
continue continue
} }

View File

@@ -101,11 +101,13 @@ func TestHTMLTemplate(t *testing.T) {
Entries []FileEntry Entries []FileEntry
DirAppend bool DirAppend bool
OutputFile string OutputFile string
IsRoot bool
}{ }{
DirName: "test-dir", DirName: "test-dir",
Entries: []FileEntry{}, Entries: []FileEntry{},
DirAppend: false, DirAppend: false,
OutputFile: "index.html", OutputFile: "index.html",
IsRoot: false,
} }
var buf bytes.Buffer var buf bytes.Buffer
@@ -161,11 +163,13 @@ func TestHTMLTemplateWithEntries(t *testing.T) {
Entries []FileEntry Entries []FileEntry
DirAppend bool DirAppend bool
OutputFile string OutputFile string
IsRoot bool
}{ }{
DirName: "test-dir", DirName: "test-dir",
Entries: entries, Entries: entries,
DirAppend: false, DirAppend: false,
OutputFile: "index.html", OutputFile: "index.html",
IsRoot: false,
} }
var buf bytes.Buffer var buf bytes.Buffer

View File

@@ -245,9 +245,9 @@ func TestProcessDirWithDirAppend(t *testing.T) {
htmlContent := string(content) htmlContent := string(content)
// Check that directory links include index.html (URL escaped) // Check that directory links include index.html
if !strings.Contains(htmlContent, "subdir%2Findex.html") { if !strings.Contains(htmlContent, "subdir/index.html") {
t.Errorf("Directory links should include index.html when DirAppend is true. Expected subdir%%2Findex.html in content") t.Errorf("Directory links should include index.html when DirAppend is true. Expected subdir/index.html in content")
} }
} }