mirror of
https://codeberg.org/git-pages/git-pages.git
synced 2026-08-28 11:56:19 +00:00
Add Create-Parents: mode to PATCH method.
This acts like `mkdir -p`, making it much less annoying to deploy e.g. documentation preview generators that use deep paths. Like before, the site must already exist: we cannot do a CAS on a non-existent manifest at the moment.
This commit is contained in:
+16
-5
@@ -452,7 +452,7 @@ func putPage(w http.ResponseWriter, r *http.Request) error {
|
||||
return err
|
||||
}
|
||||
|
||||
updateCtx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout))
|
||||
ctx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout))
|
||||
defer cancel()
|
||||
|
||||
contentType := getMediaType(r.Header.Get("Content-Type"))
|
||||
@@ -486,7 +486,7 @@ func putPage(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
result = UpdateFromRepository(updateCtx, webRoot, repoURL, branch)
|
||||
result = UpdateFromRepository(ctx, webRoot, repoURL, branch)
|
||||
|
||||
default:
|
||||
_, err := AuthorizeUpdateFromArchive(r)
|
||||
@@ -500,7 +500,7 @@ func putPage(w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
// request body contains archive
|
||||
reader := http.MaxBytesReader(w, r.Body, int64(config.Limits.MaxSiteSize.Bytes()))
|
||||
result = UpdateFromArchive(updateCtx, webRoot, contentType, reader)
|
||||
result = UpdateFromArchive(ctx, webRoot, contentType, reader)
|
||||
}
|
||||
|
||||
return reportUpdateResult(w, result)
|
||||
@@ -554,12 +554,23 @@ func patchPage(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
updateCtx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout))
|
||||
var parents CreateParentsMode
|
||||
switch r.Header.Get("Create-Parents") {
|
||||
case "", "no":
|
||||
parents = RequireParents
|
||||
case "yes":
|
||||
parents = CreateParents
|
||||
default:
|
||||
http.Error(w, "malformed Create-Parents: header", http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), time.Duration(config.Limits.UpdateTimeout))
|
||||
defer cancel()
|
||||
|
||||
contentType := getMediaType(r.Header.Get("Content-Type"))
|
||||
reader := http.MaxBytesReader(w, r.Body, int64(config.Limits.MaxSiteSize.Bytes()))
|
||||
result := PartialUpdateFromArchive(updateCtx, webRoot, contentType, reader)
|
||||
result := PartialUpdateFromArchive(ctx, webRoot, contentType, reader, parents)
|
||||
return reportUpdateResult(w, result)
|
||||
}
|
||||
|
||||
|
||||
+19
-5
@@ -12,12 +12,19 @@ import (
|
||||
|
||||
var ErrMalformedPatch = errors.New("malformed patch")
|
||||
|
||||
type CreateParentsMode int
|
||||
|
||||
const (
|
||||
RequireParents CreateParentsMode = iota
|
||||
CreateParents
|
||||
)
|
||||
|
||||
// Mutates `manifest` according to a tar stream and the following rules:
|
||||
// - A character device with major 0 and minor 0 is a "whiteout marker". When placed
|
||||
// at a given path, this path and its entire subtree (if any) are removed from the manifest.
|
||||
// - When a directory is placed at a given path, this path and its entire subtree (if any) are
|
||||
// removed from the manifest and replaced with the contents of the directory.
|
||||
func ApplyTarPatch(manifest *Manifest, reader io.Reader) error {
|
||||
func ApplyTarPatch(manifest *Manifest, reader io.Reader, parents CreateParentsMode) error {
|
||||
type Node struct {
|
||||
entry *Entry
|
||||
children map[string]*Node
|
||||
@@ -72,11 +79,18 @@ func ApplyTarPatch(manifest *Manifest, reader io.Reader) error {
|
||||
return fmt.Errorf("%w: %s: not a directory", ErrMalformedPatch, dirName)
|
||||
}
|
||||
if _, exists := node.children[segment]; !exists {
|
||||
nodeName := strings.Join(segments[:index+1], "/")
|
||||
return fmt.Errorf("%w: %s: path not found", ErrMalformedPatch, nodeName)
|
||||
} else {
|
||||
node = node.children[segment]
|
||||
switch parents {
|
||||
case RequireParents:
|
||||
nodeName := strings.Join(segments[:index+1], "/")
|
||||
return fmt.Errorf("%w: %s: path not found", ErrMalformedPatch, nodeName)
|
||||
case CreateParents:
|
||||
node.children[segment] = &Node{
|
||||
entry: NewManifestEntry(Type_Directory, nil),
|
||||
children: map[string]*Node{},
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node.children[segment]
|
||||
}
|
||||
if node.children == nil {
|
||||
dirName := strings.Join(segments[:len(segments)-1], "/")
|
||||
|
||||
+2
-1
@@ -159,6 +159,7 @@ func PartialUpdateFromArchive(
|
||||
webRoot string,
|
||||
contentType string,
|
||||
reader io.Reader,
|
||||
parents CreateParentsMode,
|
||||
) (result UpdateResult) {
|
||||
var err error
|
||||
|
||||
@@ -177,7 +178,7 @@ func PartialUpdateFromArchive(
|
||||
// `*Manifest` objects, which should never be mutated.
|
||||
newManifest := &Manifest{}
|
||||
proto.Merge(newManifest, oldManifest)
|
||||
if err := ApplyTarPatch(newManifest, reader); err != nil {
|
||||
if err := ApplyTarPatch(newManifest, reader, parents); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return newManifest, nil
|
||||
|
||||
Reference in New Issue
Block a user