Get the modification time of a global resource

Hello,

I would like to do this, but I can’t find any method for this purpose in the docs:

{{ with resources.Get "data.csv" -}}
	{{- .Lastmod }}

I need the modification time of a file I process from the assets directory. Is there a way to get this in Hugo?

The use-case is, that the CSV is rendered as a table and I would like to print under the table when the data was last refreshed.

Thanks.

I’m not aware of any method where you can get that information for a file based resource.

If your file resource is physically available in your project directory you may use os.Stat to get the last modification date. (os.Stat does not support mounted files)

 {{- with resources.Get "data.csv}}
   {{ warnf "name: %s " (os.Stat "assets/data.csv").ModTime }}

If you use some wildcard method you can use resource.Name to get the path. (read the docs for details how that behaves!!!). and then use that with os.Stat.

Maybe this would be something to ask for if its a valuable enhancement requests.

Guess that would be a bigger story and maybe quite runtime expensive.

  • Add a File method to resource (only valid when it’s backed by a file (see Page.File)
  • The fileinfo object for a file does not include the last modification date so needs to be added
  • maybe gitinfo for these, too
1 Like

In genereal its not possible/feasible.

Variants of this has a lot of discussions, and I guess it boils down to this:

  • Usually a Resource is backed by a file on disk.
  • Usually the last mod time of that file does not represent the “last edit time”
  • So enters Git etc.

So, yes, I would love if Hugo had a better story here (there are plans), but it’s not simple.

1 Like

Before building your site, run a Bash script like this:

make-git-author-date-db.sh
#!/usr/bin/env bash

#------------------------------------------------------------------------------
# @file
# Creates a database of Git author dates for each file in the assets directory.
#
# To use the database in a Hugo template:
#
#	{{ with resources.Get "a.jpg" }}
#	  {{ with index site.Data.resources.git.author_dates .Name }}
#	    {{ $tAttribute := .Format "2006-01-02T15:04:05-07:00" }}
#	    {{ $tDisplay := .Format "2006-01-02 15:04:05 -07:00" }}
#	    <p>Last modified: <time {{ printf "datetime=%q" $tAttribute | safeHTMLAttr }}>{{ $tDisplay }}</time></p>
#	  {{ end }}
#	{{ end }}
#
# Git uses a commit-graph file, stored in objects/info, as an auxiliary data
# structure to enhance the performance of history traversal. This optimization
# benefits commands such as git log, git blame, and git rev-list, particularly
# in repositories with extensive or intricate histories.
#
# With Git 2.24 and later, in its default configuration, Git updates the
# commit-graph whenever git-gc is run. To manually update the commit-graph:
#
# 	git commit-graph write --reachable --changed-paths
#
# Keep your commit-graph updated for faster script execution.
#------------------------------------------------------------------------------

main() {

  # Sensible defaults; change if needed.
  declare -r assets_dir=assets # no trailing slash
  declare -r data_dir=data/resources # no trailing slash
  declare -r outfile=GitInfo.yaml

  # Leave these alone.
  declare -a files
  declare file
  declare path

  # Verify existence of assets directory.
  if [[ ! -d "${assets_dir}" ]]; then
    >&2 echo "Error: the ${assets_dir} directory does not exist" && exit 1
  fi

  # Create the data directory.
  if [[ ! -d "${data_dir}" ]]; then
    mkdir -p "${data_dir}" || exit 1
  fi

  # Clear the data file if it exists.
  if [[ -f "${data_dir}/${outfile}" ]]; then
    true > "${data_dir}/${outfile}"
  fi

  # Create the data file.
  readarray -d '' files < <(find "${assets_dir}"  -type f -print0)
  for file in "${files[@]}"; do
    path=${file#$assets_dir/}
    echo "\"/${path}\": $(git log -1 --pretty=format:'%n  AbbreviatedHash: "%h"%n  AuthorDate: "%aI"%n  AuthorEmail: "%ae"%n  AuthorName: "%an"%n  Hash: "%H"%n  Subject: "%s"%n' "${file}")" >> "${data_dir}/${outfile}"
  done

}

set -euo pipefail
main "$@"

To create a data file like this:

data/resources/GitInfo.yaml
"/shared/other/test.js": 
  AbbreviatedHash: "973f798"
  AuthorDate: "2024-04-28T05:07:59-07:00"
  AuthorEmail: "joe.mooring@veriphor.com"
  AuthorName: "Joe Mooring"
  Hash: "973f798a136efc3ff04ec0df4f7cd007132e28e3"
  Subject: "Reorg shared files"
"/shared/office/test.xlsx": 
  AbbreviatedHash: "973f798"
  AuthorDate: "2024-04-28T05:07:59-07:00"
  AuthorEmail: "joe.mooring@veriphor.com"
  AuthorName: "Joe Mooring"
  Hash: "973f798a136efc3ff04ec0df4f7cd007132e28e3"
  Subject: "Reorg shared files"
"/shared/office/test.docx": 
  AbbreviatedHash: "973f798"
  AuthorDate: "2024-04-28T05:07:59-07:00"
  AuthorEmail: "joe.mooring@veriphor.com"
  AuthorName: "Joe Mooring"
  Hash: "973f798a136efc3ff04ec0df4f7cd007132e28e3"
  Subject: "Reorg shared files"

Then display a last modified date using something like this:

{{ with resources.Get "a.jpg" }}
  {{ with index site.Data.resources.git.author_dates .Name }}
    {{ $tAttribute := .Format "2006-01-02T15:04:05-07:00" }}
    {{ $tDisplay := .Format "2006-01-02 15:04:05 -07:00" }}
    <p>Last modified: <time {{ printf "datetime=%q" $tAttribute | safeHTMLAttr }}>{{ $tDisplay }}</time></p>
  {{ end }}
{{ end }}

For CI/CD deployments you’ll want to commit the modified data file, so the local process is:

git add -A && git commit -m "My latest changes to the repo"
./make-git-author-date-db.sh 
git add -A && git commit --amend --no-edit
git push

Or configure your deployment workflow file to run the script server side.

Performance

On an exceptionally average system, with 124 files in the assets directory, the script ran for 1.5 seconds. After garbage collection (git gc), which updates the commit-graph file, the script ran for 0.5 seconds. Update the commit-graph file on-demand with:

git commit-graph write --reachable --changed-paths
1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.