gohugoio / hugo

The world’s fastest framework for building websites.
https://gohugo.io
Apache License 2.0
74.98k stars 7.47k forks source link

Overlapping `cascade` rules are applied in nondeterministic order #12594

Open nelhage opened 3 months ago

nelhage commented 3 months ago

If multiple cascade rules match the same page, they are applied in arbitrary order according to Go map iteration order, resulting in nondeterminstic builds and extremely confusing behavior.

Context

I wanted to create a /drafts/ section on my site, with the behavior that posts there would be published to production so I could share them for review, but would only be listed locally, to discourage stumbling across them.

After some experimentation, I determined that the following content/drafts/_index.md appeared to work as desired; I speculated that overlapping cascade rules were applied in a "first-match-wins" order.

---
cascade:
- _target:
    environment: development
  build:
    list: always
- build:
    list: never
---

However, the above only mostly works; I soon discovered that sometimes posts would be excluded from the page list in development.

The bug

It turns out that rules are applied in an arbitrary order, according to Go map iteration order. In practice -- for my build of hugo -- this results in mostly consistent behavior, which is what made the behavior so confusing.

Reproducer

I've pushed a minimal demo to github:

$ git clone https://github.com/nelhage/hugo-cascade-order
$ cd hugo-cascade-order
# Render 100 times, and count how many pages appear in the RSS feed each time.
# The correct answer should be 20
$ { for _ in {1..100}; do hugo --quiet --environment development && grep -c '<title>Page' public/post/index.xml; done } | sort | uniq -c | sort -rn
  26 17
  20 18
  18 19
  12 15
  11 16
  10 20
   2 14
   1 12

You can see that links are generated and included in the page list most of the time, but that typically 1-3 pages are dropped. Go deliberately injects entropy into the map iteration order, but apparently in this case it is mostly deterministic.

Comment

The issue can be worked around in user sites by eliminating overlapping cascade rules -- e.g. I have worked around using

---
cascade:
- _target:
    environment: development
  build:
    list: always
- _target:
    environment: '{staging,production}'
  build:
    list: never
---

However, I still regard this behavior as a bug due to how profoundly confusing and unexpected it is. cascade: rules are present in the source as a list, and so IMO it's very reasonable to expect them to be processed in that order.

What version of Hugo are you using (hugo version)?

$ hugo version
hugo v0.125.5+extended darwin/arm64 BuildDate=unknown VendorInfo=nixpkgs

Does this issue reproduce with the latest release?

Yes

nick2432 commented 2 months ago

can i work on this

bep commented 2 months ago

@nick2432 this issue isn't very straight forward, so I would prefer to do this myself.

nick2432 commented 2 months ago

@nick2432 this issue isn't very straight forward, so I would prefer to do this myself.

can you suggest any other good first issue or simple issue?