Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions cli/azd/pkg/project/project_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package project

import (
"bytes"
"errors"
"fmt"
"io/fs"
Expand All @@ -30,12 +31,19 @@ func createDeployableZip(svc *ServiceConfig, root string) (string, error) {
ignoreFile := svc.Host.IgnoreFile()
var ignorer gitignore.GitIgnore
if ignoreFile != "" {
ig, err := gitignore.NewFromFile(filepath.Join(root, ignoreFile))
if !errors.Is(err, fs.ErrNotExist) && err != nil {
ignoreFilePath := filepath.Join(root, ignoreFile)
contents, err := os.ReadFile(ignoreFilePath)
if errors.Is(err, fs.ErrNotExist) {
// no ignore file, use defaults below
} else if err != nil {
zipFile.Close()
os.Remove(zipFile.Name()) //nolint:gosec // G703: zipFile.Name() is our own temp file, not user-controlled
return "", fmt.Errorf("reading ignore file: %w", err)
} else {
// Strip UTF-8 BOM if present, then parse from in-memory contents.
contents = stripUTF8BOM(contents)
ignorer = gitignore.New(bytes.NewReader(contents), root, nil)
}

ignorer = ig
}

// apply exclusions for zip deployment
Expand Down Expand Up @@ -68,9 +76,11 @@ func createDeployableZip(svc *ServiceConfig, root string) (string, error) {
}

// apply exclusions from ignore file
if ignorer != nil && ignorer.Absolute(src, isDir) != nil {
return false, nil
} else if ignorer == nil { // default exclusions without ignorefile control
if ignorer != nil {
if match := ignorer.Absolute(src, isDir); match != nil && match.Ignore() {
return false, nil
}
} else { // default exclusions without ignorefile control
if svc.Language == ServiceLanguagePython {
if isDir {
// check for .venv containing pyvenv.cfg
Expand Down Expand Up @@ -112,3 +122,12 @@ func createDeployableZip(svc *ServiceConfig, root string) (string, error) {

return zipFile.Name(), nil
}

// utf8BOM is the byte order mark that some Windows editors prepend to UTF-8 files.
var utf8BOM = []byte{0xEF, 0xBB, 0xBF}

// stripUTF8BOM removes a leading UTF-8 BOM from the given byte slice if present.
// The BOM breaks gitignore pattern parsing because the invisible bytes become part of the first pattern.
func stripUTF8BOM(data []byte) []byte {
return bytes.TrimPrefix(data, utf8BOM)
}
5 changes: 5 additions & 0 deletions cli/azd/pkg/project/service_target_functionapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func resolveFunctionAppRemoteBuild(serviceConfig *ServiceConfig) (remoteBuild bo
return false, fmt.Errorf("reading ignore file: %w", err)
}

// Strip UTF-8 BOM if present. Some Windows editors (e.g. Notepad) prepend a BOM which
// causes the gitignore parser to treat the first pattern as having invisible prefix bytes,
// breaking pattern matching.
ignoreFileContents = stripUTF8BOM(ignoreFileContents)

// Parse from in-memory contents so we don't hold an open file handle (important on Windows temp dirs).
ignore := gitignore.New(bytes.NewReader(ignoreFileContents), serviceConfig.Path(), nil)

Expand Down
Loading
Loading