Point-in-time source links across Hugo modules
When referencing source code that I’d written, I wanted to be able to link to the file and have it reference the file at “that point in time.”
Assuming no bugs and that I have kept this site well maintained - that link should show the current version of my link render logic on hugo.
Why?
- I might move my files around (in fact, I intend to move my
/blogdir to/siteat some point) - The code might have moved on.
- I might one day, have removed the bit of code or functionality that I was pointing to
I had this issue enough times on other blogs and it annoyed me something fierce.
The linking itself is fairly straightforward, though there is a bunch of logic to figure out if it is link to source code:
| |
The main problem with this approach is that code needs to be in the same repo. This was fine for a while - but I felt it was unreasonable to expect people to download the entire monorepo if they were only interested in a small part of it.
I considered ditching it. There were only a handful of posts that actually linked to source code, but I was convinced that it was worth it.
The plan
The core idea is to split out the repos, then put the blog posts in the repos with the code. We then bring all of these posts into one central place before publishing.
The core requirements were:
- The commit id tagged to the post needs to come from the source repo
- Ideally, live refresh should continue to work so you can edit files and see the changes live
hugo supports bringing in content from other places. However, GitInfo did
not work for it.
The constraint throughout: whatever the solution, I wanted to add as few moving parts as possible.
stamp and stage pipeline
One solution I thought about was to implement a pipeline of some form. Each downstream repo would be put through a preprocessor, which would stamp file frontmatter with the commit id and repo url before bringing it into hugo.
It would then be a simple case of updating the templates and letting hugo render it.
It would be too complicated to maintain though:
- script to “stamp” frontmatter
- copying all the content across to one place
- then, the usual build and deploy
It would also not support live refresh.
It didn’t quite resonate, so I let it sit.
content adapters
A few days ago, I decided to take another stab at solving this. Content adapters came up as one option. This could keep it all in hugo.
It would do the same as above - stamp it, then bring it in, but no extra script or pipelines to manage or maintain - hugo could do the whole thing.
However:
- There is still the stamping step,
- but it could be self-contained code in a repo
- No live refresh on editing downstream posts
I was going to go ahead with this plan, before I decided to do one last check to
see if hugo had improved their GitInfo support for external repos.
back to hugo submodule
When I checked whether hugo had fixed the issue with GitInfo in downstream
modules, I found #5533
which was
closed. I took it this as a good sign.
It took a bunch of trial and error to understand the following:
- This only worked with git repos
- local module mounting did not provide correct
GitInfo
- local module mounting did not provide correct
- I discovered a bug
(reported as #1492
)
- it required the downstream go module to be at the root
I could work around these issues.
I had written a post for henge which seemed like the perfect candidate to test this on.
| |
for henge (oops, I can’t link to that one from here, with a commit id, but I’ll live)
I created a
go.mod
(don’t get excited - I added the full link manually) in the root (and will later
move it into /site)
In the same repo, you will find site/content which has the henge endeavour as
well as the post.
It worked. The only problem - it didn’t do live refresh.
There was an easy fix for it though:
| |
I had the henge repo locally anyway, so it just worked.
I’ll put the hugo server command into a shell so that it can define all the mod replacements in there when I have more downstream ones.
Automating the build had a couple of steps.
refresh downstream modules on build
In the forgejo actions, , I added a step to get the module @ main, before building.
Otherwise, it’ll only pull in the version from the last hugo mod get
| |
trigger upstream build on content push
Triggering an upstream build on content changes was also easy:
| |
You’ll need to get an access token from your upstream repo and add it as a secret here.
Conclusion
The final solution met both requirements and as an added bonus has minimal maintenance burden.
Compared to a monorepo, however, I cannot reference code if it is in another “project.” I can work around this by linking it manually if the other repo content is already live.
Linking manually is trickier if the code is in the same repo because the post and the code is often published in the same PR, which is one of the reasons for this little bit of functionality.
