etalla
May 12, 2021, 10:20pm
1
I’m using .Site.Data
and {{ range }}
to fetch and loop through data from JSON files in my data folder.
It works great.
My question is: For each JSON file, I’d like to display the timestamp of the file’s last commit.
I see Hugo makes this info available to templates using .LastMod
when enabling enableGitInfo
in config
.
Is it possible at all to also fetch .LastMod
for each JSON file, and if so, how? The files are versioned so Git would have the info.
As FYI, my loop:
{{ range $i, $person := sort $.Site.Data.people "Name" }}
<tr>
{{ range $k, $prop := $.Page.Params.headers }}
{{ if isset $person $prop }}
<td>{{ index $person $prop }}</td>
{{ end }}
{{ end }}
</tr>
{{ end }}
</tbody>```
.Lastmod
is a .Page
property. The data files are not pages. So we need to find another way.
Option 1: Get the modification time from the file system
This is not the same as the Git author date, but it’s easy to do.
{{ (os.Stat "data/a.json").ModTime }}
Option 2: Create a metadata file
Run a Bash script before each build that creates/updates a metadata file in the data directory.
Bash script
#!/usr/bin/env bash
main() {
declare file_mod_date
declare git_author_date
declare key
declare path
declare paths
declare metadata="data/metadata.toml"
# Create/empty metadata file.
true > "${metadata}"
# Get paths to all data files, recursively.
readarray -d '' paths < <(find data -type f -print0) || \
{ echo "Error: unable to create array of paths."; exit 1; }
# Build TOML file.
for path in "${paths[@]}"; do
git_author_date=$(git log -1 --pretty="format:%cI" "${path}") || \
{ echo "Error: unable to obtain the Git author date."; exit 1; }
file_mod_date=$(date -r "${path}" "+%Y-%m-%dT%H:%M:%S%:z") || \
{ echo "Error: unable to obtain the file mod date."; exit 1; }
key=${path%.*} # Remove extension
key=${key/data\//} # Remove the leading "data/"
key=${key//\//.} # Replace all / with .
printf "[%s]\\n" "${key}" >> "${metadata}"
if [[ ${#file_mod_date} -gt 0 ]]; then
printf "file_mod_date = %s\\n" "${file_mod_date}" >> "${metadata}"
fi
if [[ ${#git_author_date} -gt 0 ]]; then
printf "git_author_date = %s\\n" "${git_author_date}" >> "${metadata}"
fi
printf "path = \"%s\"\\n\\n" "${path}" >> "${metadata}"
done
}
set -euo pipefail
main "$@"
For example, with this data structure:
data
├── sub/
│ └── c.json
├── a.json
└── b.json
the script creates this data/metadata.toml file:
[sub.c]
file_mod_date = 2021-05-12T18:58:21-07:00
git_author_date = 2021-05-13T00:00:12-07:00
path = "data/sub/c.json"
[b]
file_mod_date = 2021-05-12T17:45:30-07:00
git_author_date = 2021-05-13T00:00:12-07:00
path = "data/b.json"
[a]
file_mod_date = 2021-05-12T17:44:37-07:00
git_author_date = 2021-05-13T00:00:12-07:00
path = "data/a.json"
and you can get the Git author date with:
{{ site.Data.metadata.a.git_author_date }}
{{ site.Data.metadata.b.git_author_date }}
{{ site.Data.metadata.sub.c.git_author_date }}
or with the file modification time as a fallback:
{{ with site.Data.metadata.a.git_author_date }}
{{ . }}
{{ else }}
{{ site.Data.metadata.a.file_mod_date }}
{{ end }}
3 Likes
I did this once but it’s a bit of a hack.
I’m making a site where I need to post the most recent update for something. While .GitInfo is there, it only works for pages in content directories.
To get around this, I mapped the file I wanted the GitInfo for to the content directory.
module:
mounts:
- source: file.txt
target: content/shame/file.md
I also created a shame/_index.md file to make sure it never gets placed in the output:
---
headless: true
cascade:
draft: false
_build:
render: false
li…
The idea is to use Hugo modules to make the json file look like a regular page which will then have lastmod.
1 Like
Thank you for this incredibly thorough and detailed reply, I didn’t expect this! The bash script was spot on. Having never written one myself I really appreciated the sample you provided. Works perfectly.
1 Like
Thank you! I went with the bash script suggested by jmooring
but this was an interesting approach.
1 Like
system
Closed
May 15, 2021, 7:25pm
6
This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.