Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add infrastructure to redirect anchors #740

Open
fricklerhandwerk opened this issue Oct 5, 2023 · 17 comments
Open

Add infrastructure to redirect anchors #740

fricklerhandwerk opened this issue Oct 5, 2023 · 17 comments
Labels
good first issue site Improvements to the site infrastructure or content presentation

Comments

@fricklerhandwerk
Copy link
Collaborator

fricklerhandwerk commented Oct 5, 2023

Problem
When we move sections from one file to another, there is no way to automatically redirect users to the new location. Using stale anchor links will lead nowhere.

Approaches
The Nix manual uses a client-side redirect script we could copy.
It should be easy to incorporate it into the site configuration.

There doesn't seem to be comparable pre-made tooling for Sphinx, judging from a quick internet search.

Willing to help?
Always.

Priorities

Add 👍 to issues you find important.

@fricklerhandwerk fricklerhandwerk added site Improvements to the site infrastructure or content presentation good first issue labels Oct 5, 2023
@yukiisbored
Copy link
Member

What if we simply create the old anchors in the document itself instead of redirecting users?

https://www.sphinx-doc.org/en/master/usage/referencing.html#role-ref

@fricklerhandwerk
Copy link
Collaborator Author

This will work for references in text, but not for URLs used in the wild.

@aos
Copy link

aos commented Nov 4, 2023

Hi @fricklerhandwerk I would like to work on this, but was curious if there's any docs on how nix.dev is hosted. From my understanding and digging through the code: it's a static site generated by sphinx, which is hosted on cloudflare pages. Is this correct? If so, where would this (new) redirect script sit?

@fricklerhandwerk
Copy link
Collaborator Author

fricklerhandwerk commented Nov 5, 2023

It's now deployed to Netlify thanks to @delroth. That shouldn't be relevant for anchor redirects though, as this can only happen client side.

IIUC it also shouldn't matter where the file sits, as you'd have to wire it up in conf.py to be included in every HTML page, and that's where you reference its location.

@aos
Copy link

aos commented Nov 6, 2023

Ah okay got it - will send a PR soon!

@aos
Copy link

aos commented Nov 10, 2023

Howdy - I started to dig into this. Initially I was going down the path of using the redirects.js file in the Nix manual, but its "algorithm" is tailored to mdBook and path_to_root. This site uses sphinx instead to generate the site. I could rewrite it and inject a new redirects.js file into each section, but I got the feeling that this should've already been a solved problem. I looked around and found a few solutions (sphinx extensions):

  1. https://github.com/sphinx-contrib/redirects: This seems to be the simplest (which you sort of already have, and maybe we can write a script to generate the equivalent file that this extension wants), uses JS under the hood
  2. https://github.com/documatt/sphinx-reredirects: A bit more involved, uses relative/nested paths, which might not make too much sense in our case. Creates a simple HTML file in the old path with a redirection to the new location

I want to say that this is the problem you're trying to solve, but let me know if I'm way off or something! If it is, I'm inclined to test out solution 1 first and see how that goes

@fricklerhandwerk
Copy link
Collaborator Author

fricklerhandwerk commented Nov 10, 2023

Thanks for the digging!

Both seem to work at the file level, and indeed we're already using the first. What we're lacking is a way to change heading IDs (the URL fragment part) without disorienting users, which is especially important on large pages.

@stablejoy
Copy link
Contributor

Hello. By looking at the list of redirects at [nix.dev/blob/master/_redirects](https://github.com/NixOS/nix.dev/blob/master/_redirects) and the script provided I am first thinking something like:

const redirects = {
 "index.html": {
    "old-section": "new-section",
    "/tutorials/install-nix": "/install-nix",
    // additional mappings
};

Now what is the difference between path redirects and anchor redirects since this doesn’t look quite right?

  • Path redirect:
    • A user tries to visit https://example.com/tutorials/install-nix, but this content has been moved to https://example.com/install-nix. A path redirect ensures the user is taken to the new URL.
  • Anchor redirect:
    • A user clicks a link to https://example.com/documentation#installation, but the #installation anchor has been renamed to #install-guide. An anchor redirect scrolls the user to the #install-guide part of the page automatically.

Now I see that there are no anchor links such as #create-a-shell-environment in the redirect file so does that mean the anchor links would have to be declared as a redirect object in the script? Does that mean that there would have to be two redirect objects like const pathRedirects and const anchorRedirects. Am I going in the right direction? Thanks

@fricklerhandwerk
Copy link
Collaborator Author

fricklerhandwerk commented Feb 8, 2024

@stablejoy thanks for asking! You described the requirements correctly. The _redirects file cannot capture anchors though: it's used by the host (Netlify) to handle server side redirects, and those don't see URL fragments. Therefore we need an extra file that handles client side redirects – it will only work with JavaScript enabled, but that's better than nothing.

There is now also the first entry in _redirects which points from an "artificial" permalink (i.e. its "location" never physically existed) to a section anchor. This doesn't require scripting on the client, but that's another thing we need to keep track of it manually. It's very error-prone.

Ideally (...)= references would resolve to such permalinks, but Sphinx is not equipped for that. What we need in the long is document rendering framework that does all these things correctly and transparently, but there are non-trivial costs to innovation and migration in a running system...

@stablejoy
Copy link
Contributor

stablejoy commented Feb 8, 2024

Therefore we need an extra file that handles client side redirects – it will only work with JavaScript enabled, but that's better than nothing.

Does that mean we copy the existing functionality of the nix manual client-redirect script and simply add the nix.dev anchors and use that? Something like:

const redirects = {
  "/tutorials/install-nix": {
    path: "/install-nix",
    anchors: {
      "verify-installation": "new-verify-installation-anchor" 
    }
  },
  // Additional mappings here
};

Now I am curious about these old anchors. I am looking how to identify and map them. For now I am looking through the nix.dev github repo.

@fricklerhandwerk
Copy link
Collaborator Author

fricklerhandwerk commented Feb 8, 2024

Does that mean we copy the existing functionality of the nix manual client-redirect script and simply add the nix.dev anchors and use that?

Exactly.

But I'd say no need to overthink it. Having the infrastructure to be able to track future relocations would already be very good. If we happen to find old URLs that went out of date, we can always add redirects after the fact.

@stablejoy
Copy link
Contributor

Thanks. I'll be working on this, and looking to help out more with the documentation as well.

@stablejoy
Copy link
Contributor

stablejoy commented Feb 18, 2024

I have a local working nix.dev repository, and I'm viewing it locally. First I look how to extract the anchor links. By looking at the markdown files I see that the anchor links are not in any way marked. If we look at install-nix.md which is at https://nix.dev/install-nix we see that the anchor link verify installation is written as ## Verify installation in the markdown code.

Example: The page install-nix has two anchor links: Install Nix and Verify installation. How do we extract all the others as well? So I modify my request and ask the LLM to use a combination of linux utilities:

we have to modify our script. So by looking at one example page install-nix.md I see that the anchor links are not marked with # but they are like titles so for example the Verify installation is written as ## Verify installation so can you modify the script so that it searches and extracts and saves into a file all the titles that have two hashtags or pound characters such is ## Verify installation.

> find ~/code/nix.dev/source -type f -name '*.md' -exec grep -h '^## ' {} + > anchor_links.txt

I also count the lines of the saved anchor-links.txt with

> wc -l anchor_links.txt
177 anchor_links.txt

Now, I need to manually test a random page such as https://nix.dev/tutorials/packaging-existing-software. I will compare a specific line with the anchor links file and use grep to search for that line:

> grep 'Packaging existing software with Nix' ~/code/nix.dev/source/anchor_links.txt

It outputs nothing which may only mean only the first anchor that is titled same as the page isn't saved in the file. I try now with one of the sub anchors within the page.

> grep 'What do you need?' ~/code/nix.dev/source/anchor_links.txt
## What do you need?
## What do you need?
## What do you need?

Now I am not sure why there are three of those so I list all the md files within the source directory. The find command will also search through subdirectories: find . -type f -name '*.md' and I manually check the Packaging existing software page. I see that I have to modify my first script because the anchor links are not just the ones with two hash symbols. They have one, two and three hash symbols as well. That's why the first anchor was not included in the first run. So we modify the script to include #, ##, and ###

> find ~/code/nix.dev/source -type f -name '*.md' -exec grep -h '^#\+#* ' {} + > anchor_links.txt
> wc -l anchor_links.txt
350 anchor_links.txt

Yay! so instead of 177 we have 350 anchors now. Let's test it:

> grep 'Packaging existing software with Nix' ~/code/nix.dev/source/anchor_links.txt
# Packaging existing software with Nix
> grep 'What do you need?' ~/code/nix.dev/source/anchor_links.txt
### What do you need?
## What do you need?
### What do you need?
### What do you need?
## What do you need?
## What do you need?
### What do you need?
### What do you need?

Now I am curious where are the locations of these six lines of "What do you need?" because for example when I look through the Packaging existing software I only see one "What do you need?"

> grep -n 'What do you need?' ~/code/nix.dev/source/anchor_links.txt
36:### What do you need?
80:## What do you need?
95:### What do you need?
124:### What do you need?
188:## What do you need?
199:## What do you need?
210:### What do you need?
247:### What do you need?

We look for the locations of the files now with:

> grep -rn --include='*.md' 'What do you need?' ~/code/nix.dev/source

/home/stablejoy/code/nix.dev/source/contributing/documentation/writing-a-tutorial.md:31:### What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/cross-compilation.md:15:## What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/first-steps/declarative-shell.md:31:### What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/module-system/module-system.md:30:### What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/nixos/integration-testing-using-virtual-machines.md:10:## What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/nixos/nixos-configuration-on-vm.md:14:## What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/packaging-existing-software.md:27:### What do you need?
/home/stablejoy/code/nix.dev/source/tutorials/nix-language.md:69:### What do you need?

Ok, so this tells us these are all different files. "What do you need?" is indeed a sort of guiding or instructional phrase used at different points within the documentation.

What is the next step? We have our anchors link file that has all the titles. Aha, titles. Maybe we need to strip out the # symbols from the file. Let's see the provided redirect script from the nix manual. I see I have to remodify my script again. The problem with the current anchors file is that it does not have any kind of logic to it. If I have to strip out the # symbols I will have six "what do you need?" anchors with no way to map them to their correct pages. So what I need now is a new anchors list that will also encapsulate the page and the anchors as well. LLM suggests another script. Let's see how it outputs the install nix page:

/home/stablejoy/code/nix.dev/source/install-nix.md:# Install Nix
/home/stablejoy/code/nix.dev/source/install-nix.md:## Verify installation

Looks good. But when I wc -l detailed_anchors_list.txt I see that it has 342 lines and our first anchor_links.txt has 350. Let's see where is the difference.

# For detailed_anchors_list.txt
awk -F':' '{print $2}' detailed_anchors_list.txt | sed 's/^[\# ]*//g' | sort > processed_detailed_anchors.txt

# For anchor_links.txt
cat anchor_links.txt | sed 's/^[\# ]*//g' | sort > processed_anchor_links.txt

# Compare the processed files
diff processed_detailed_anchors.txt processed_anchor_links.txt

Now for example the output tells me that > Additional attributes is missing from our first script.

diff processed_detailed_anchors.txt processed_anchor_links.txt
11a12
> Additional attributes
...
  • A line beginning with > indicates a line that exists only in the second file.
  • 11a12 means at line 11 in the first file, a line was added to get to the line 12 in the second file, which is "Additional attributes".

Oh my, I check the original anchor_links.txt and I see that the Additional attributes has four hash symbols before it. So this means the script has to also include up to four levels of hash symbols.

find ~/code/nix.dev/source -type f -name '*.md' -exec awk -v OFS=':' '
    /^# / || /^## / || /^### / || /^#### / {print FILENAME, $0}
' {} + > detailed_anchors_list_with_fourth_level.txt

Now we see that we have 350 as in the original output of anchors-links.txt. OK let's look at the way anchors are printed. Our install nix test example from before looks like:

home/stablejoy/code/nix.dev/source/install-nix.md:# Install Nix
/home/stablejoy/code/nix.dev/source/install-nix.md:## Verify installation

Now how do I turn something like/home/stablejoy/code/nix.dev/source/install-nix.md:# Install Nix into "/install-nix": "/#install-nix", LLM suggests a python script to generate a javascript file with formatted mappings. We can use the anchors list we have with its 350 lines of links as input. Now our first iteration:

const redirects = {
  "/acknowledgments/sponsors": "/acknowledgments/sponsors#-sponsors",
  "/acknowledgments/index": "/acknowledgments/index#-acknowledgements",
  "/reference/index": "/reference/index#-reference",
  "/reference/glossary": "/reference/glossary#-glossary",
  "/reference/pinning-nixpkgs": "/reference/pinning-nixpkgs#-examples",
  "/reference/nix-manual": "/reference/nix-manual#-nix-reference-manual",
  "/install-nix": "/install-nix#-verify-installation",
  "/recommended-reading": "/recommended-reading#-other-videos",
  "/contributing/how-to-contribute": "/contributing/how-to-contribute#-contribute-to-nixos",
  "/contributing/how-to-get-help": "/contributing/how-to-get-help#-other-venues",
  "/contributing/index": "/contributing/index#-contributing",
  "/contributing/documentation/diataxis": "/contributing/documentation/diataxis#-guides-vs.-tutorials",
  "/contributing/documentation/writing-a-tutorial": "/contributing/documentation/writing-a-tutorial#-expand-on-the-outline",
  "/contributing/documentation/index": "/contributing/documentation/index#-licensing-and-attribution",
  "/contributing/documentation/style-guide": "/contributing/documentation/style-guide#-this-is-a-section",
  "/concepts/faq": "/concepts/faq#-are-there-any-impurities-left-in-sandboxed-builds?",
  "/concepts/index": "/concepts/index#-concepts",
  "/concepts/flakes": "/concepts/flakes#-flakes",
  "/tutorials/cross-compilation": "/tutorials/cross-compilation#-next-steps",
  "/tutorials/first-steps/declarative-shell": "/tutorials/first-steps/declarative-shell#-next-steps",
  "/tutorials/first-steps/ad-hoc-shell-environments": "/tutorials/first-steps/ad-hoc-shell-environments#-next-steps",
  "/tutorials/first-steps/reproducible-scripts": "/tutorials/first-steps/reproducible-scripts#-next-steps",
  "/tutorials/first-steps/index": "/tutorials/first-steps/index#-first-steps",
  "/tutorials/first-steps/towards-reproducibility-pinning-nixpkgs": "/tutorials/first-steps/towards-reproducibility-pinning-nixpkgs#-next-steps",
  "/tutorials/module-system/module-system": "/tutorials/module-system/module-system#-wrapping-up",
  "/tutorials/nixos/deploying-nixos-using-terraform": "/tutorials/nixos/deploying-nixos-using-terraform#-next-steps",
  "/tutorials/nixos/continuous-integration-github-actions": "/tutorials/nixos/continuous-integration-github-actions#-next-steps",
  "/tutorials/nixos/building-and-running-docker-images": "/tutorials/nixos/building-and-running-docker-images#-next-steps",
  "/tutorials/nixos/installing-nixos-on-a-raspberry-pi": "/tutorials/nixos/installing-nixos-on-a-raspberry-pi#-next-steps",
  "/tutorials/nixos/building-bootable-iso-image": "/tutorials/nixos/building-bootable-iso-image#-next-steps",
  "/tutorials/nixos/integration-testing-using-virtual-machines": "/tutorials/nixos/integration-testing-using-virtual-machines#-next-steps",
  "/tutorials/nixos/nixos-configuration-on-vm": "/tutorials/nixos/nixos-configuration-on-vm#-next-steps",
  "/tutorials/nixos/index": "/tutorials/nixos/index#-nixos",
  "/tutorials/packaging-existing-software": "/tutorials/packaging-existing-software#-next-steps",
  "/tutorials/nix-language": "/tutorials/nix-language#-learn-more",
  "/tutorials/index": "/tutorials/index#-tutorials",
  "/tutorials/file-sets": "/tutorials/file-sets#-conclusion",
  "/guides/troubleshooting": "/guides/troubleshooting#-how-to-fix:-`writing-to-file:-connection-reset-by-peer`",
  "/guides/best-practices": "/guides/best-practices#-reproducible-source-paths",
  "/guides/faq": "/guides/faq#-how-to-bootstrap-nixos-inside-an-existing-linux-installation?",
  "/guides/index": "/guides/index#-guides",
  "/guides/recipes/sharing-dependencies": "/guides/recipes/sharing-dependencies#-next-steps",
  "/guides/recipes/dependency-management": "/guides/recipes/dependency-management#-next-steps",
  "/guides/recipes/python-environment": "/guides/recipes/python-environment#-next-steps",
  "/guides/recipes/direnv": "/guides/recipes/direnv#-automatic-environment-activation-with-`direnv`",
  "/guides/recipes/index": "/guides/recipes/index#-recipes",
  "/index": "/index#-who-is-nix-for?"
};

And our install nix page has two anchors actually not just one. So it seems we need more anchors. Let's moditfy the script to include all the lines of our anchors list. The script would be:

import json
from collections import defaultdict

# Specify the path to your detailed anchors list file and the output file
input_file = 'detailed_anchors_list_with_fourth_level.txt'
output_file = 'redirects.js'

# Initialize a dictionary to hold the redirects, using defaultdict for nested dictionaries
redirects = defaultdict(dict)

with open(input_file, 'r') as f:
    for line in f:
        # Assuming the line format is: /path/to/file.md:# Anchor Name
        file_path, anchor_text = line.strip().split(':', 1)
        # Convert the file path to a simplified web path
        web_path = file_path.split('/')[-1].replace('.md', '.html')
        # Format the anchor name to a web-friendly ID
        anchor_id = anchor_text.strip().lstrip('#').lower().replace(' ', '-').replace('/', '-')
        
        # Assuming each anchor should link to a specific section on the new page
        # The new path could be adjusted based on your directory structure or URL scheme
        new_path = f"{web_path}#{anchor_id}"
        
        # Map from the anchor ID to the new path
        # Note: If your logic for determining the new_path is different, adjust accordingly
        redirects[web_path][anchor_id] = new_path

# Convert the redirects dictionary to a JavaScript object notation
redirects_js = f"const redirects = {json.dumps(dict(redirects), indent=2)};"

# Write the JavaScript code to the output file
with open(output_file, 'w') as f:
    f.write(redirects_js)

print(f"Redirects have been written to {output_file}")

And now our output looks better:

const redirects = {
  "sponsors.html": {
    "-sponsors": "sponsors.html#-sponsors"
  },
  "index.html": {
    "-acknowledgements": "index.html#-acknowledgements",
    "-reference": "index.html#-reference",
    "-contributing": "index.html#-contributing",
    "-contributing-documentation": "index.html#-contributing-documentation",
    "-reference-manuals": "index.html#-reference-manuals",
    "-nix.dev": "index.html#-nix.dev",
    "-nixos.org": "index.html#-nixos.org",
    "-communication-channels": "index.html#-communication-channels",
    "-matrix": "index.html#-matrix",
    "-discourse": "index.html#-discourse",
    "-meetings-and-events": "index.html#-meetings-and-events",
    "-external-sources": "index.html#-external-sources",
    "-wiki": "index.html#-wiki",
    "-nix-pills": "index.html#-nix-pills",
    "-licensing-and-attribution": "index.html#-licensing-and-attribution",
    "-concepts": "index.html#-concepts",
    "-first-steps": "index.html#-first-steps",
    "-nixos": "index.html#-nixos",
    "-tutorials": "index.html#-tutorials",
    "-guides": "index.html#-guides",
    "-recipes": "index.html#-recipes",
    "-welcome-to-nix.dev": "index.html#-welcome-to-nix.dev",
    "-what-can-you-do-with-nix?": "index.html#-what-can-you-do-with-nix?",
    "-who-is-nix-for?": "index.html#-who-is-nix-for?"
  },
...
"install-nix.html": {
    "-install-nix": "install-nix.html#-install-nix",
    "-verify-installation": "install-nix.html#-verify-installation"
  },
...

How does this look like? This was just an update since it has been for more than a week since I wrote I will work on this. I find the problem super interesting and I am learning a lot. Also thank you very much for opening this issue.

Update: I think I have to remove the dashes from the output so I made a new iteration with the python script to strip those down. Now we have:

...
"install-nix.html": {
    "install-nix": "install-nix.html#install-nix",
    "verify-installation": "install-nix.html#verify-installation"
  },
...

Update: Now we have to append the js logic to these mappings and wire this script into the site. We are instructed to:

IIUC it also shouldn't matter where the file sits, as you'd have to wire it up in conf.py to be included in every HTML page, and that's where you reference its location.

We will place our JavaScript file (redirects.js) in the _static directory of the site. Then we have to update the conf.py. Let’s see how conf.py reads the _static directory:

html_static_path = ['_static', 'tutorials/module-system/files']

We need to see through the file to find where other HTML-related configurations are set, such as html_theme, html_static_path and append our script there.

# -- Options for HTML output ----------------------------------------------

html_baseurl = "https://nix.dev/"
html_theme = "sphinx_book_theme"

Replace the existing redirects object in the nix manual script with our own nix.dev mappings and add it to conf.py:

html_js_files = [
    'redirects.js',
]

Let's see how this works:
Run nix-shell --run devmode and open a browser at http://localhost:5500.
Now I have to think again how to test this because I'm not sure if I'm done here.

Update: Wonderful. It's not working? First I forgot to add the redirect.js file to the _static directory. And now when I test the redirects I get:

◈ Static server listening to 3999

(dev.command completed in 7ms)
◈ Redirects syntax errors:
Could not parse redirect line 5:
  const redirects = {
The destination path/URL must start with "/", "http:" or "https:"

Could not parse redirect line 34:
  }
Missing destination path/URL

Could not parse redirect line 36:
  let segments = document.location.pathname.split('/');
The destination path/URL must start with "/", "http:" or "https:"
...

Now I will test it further and see what's wrong.

Update: I have compared the redirect.js with the one from nix manual. It seems I have to do another iteration of the script since it is missing the slash character. This slash signifies the root of the current directory, ensuring the browser correctly interprets the anchor as part of the URL. Let's test it now again with a new iteration of the python script that will have:

...
new_path = f"{web_path}/#{anchor_id}"  # Include the slash before the hash
...

OK, I'm stuck for now, have to explore more, it's like the server and client logic is mixing. I will explore now the nix manual original redirect script to understand better how it works.

UPDATE:

I look again at the nix manual repository and search for any mention of redirect.js at
https://github.com/search?q=repo%3ANixOS%2Fnix+redirects.js&type=code where we see that redirect.js
is wired in doc/manual/book.toml

[output.html]
additional-css = ["custom.css"]
additional-js = ["redirects.js"]
...

In nix manual source repository there is also the _redirect rules for server-side just as we have one for the nix.dev.
Ok let's actually look at the nix manual and take out a small example from it. We will look at
"builtin-attrNames": "language/builtins.html#builtins-attrNames", from the redirect.js which is at
https://nixos.org/manual/nix/stable/language/builtins#builtins-attrNames and for sure when we look at our script:

    "acknowledgements": "index.html/#acknowledgements",
    "reference": "index.html/#reference",
    "contributing": "index.html/#contributing",

It doesn't look quite right since index.html is repeatedly referenced at each line. Let's iterate on our script again. And we have now:

const redirects = {
  "sponsors.html": {
    "sponsors": "#sponsors"
  },
  "index.html": {
    "acknowledgements": "#acknowledgements",
    "reference": "#reference",
    "contributing": "#contributing",
...

Let's see how it works now. It still shows errors but I may be missing something.

  • netlify dev is for testing the build and server-side aspects of your site, including server-side redirects. It's not intended to validate the syntax of your client-side JavaScript.
  • Let's see if we can test the site with a browser and developer tools.

I see where is the problem. Oh, our site structure includes multiple pages that themselves contain anchors, and the script did not account for the hierarchical organization of these pages and their subpages.

I've updated redirect.js for page hierarchy and manually tested redirection by changing some of the key values to see if the redirect will work on the correct anchor. Redirects to pages work, but auto-scrolling to specific sections fails. I need to adjust again the hierarchy and study the nix manual redirect script.

@fricklerhandwerk
Copy link
Collaborator Author

@stablejoy thanks for digging into it. Have you considered first building the site and then grepping for id="(.*)"? It would be super cool if we had a pre-commit hook (and corresponding CI check) that would track changes to anchors and make sure they are reflected in the redirects. But having a redirects file to begin with would already be great.

I suggest you open a pull request, since your work log here is a lot to chew on but can't be meaningfully reviewed as a code contribution.

@DanielSidhion
Copy link
Member

I think looking for all elements with an id in the HTML output will do the job - after digging a bit, seems like some elements with a name attribute could also be targets of an URL with a hash fragment, but that is deprecated.

I'm interested in this because I'm looking for a solution to this same problem for the Nixpkgs/NixOS manual as well (both the redirection part and figuring out all possible links that a certain version of the manual could have).

@fricklerhandwerk did you think about how a CI check would track changes to anchors? I initially thought about grabbing all possible links from a previous commit and comparing with the commit being checked by the CI, but this is tricky. My next idea was to dump all the possible links in a file that is hosted along with the documentation website, and make the CI compare the links against that:

  • For nix.dev, CI would grab all possible links from the current documentation it's testing, and then grab all current links from e.g. https://nix.dev/.well-known/all_links or whatever
  • If there are any links in https://nix.dev/.well-known/all_links that don't exist in the current commit being checked, fail the CI test

@stablejoy if/when you make any PRs about this, please ping me as well

@fricklerhandwerk
Copy link
Collaborator Author

Another option is to have a file in the repo which records the previously existing URLs. All of that is a big hack anyway.

In my humble opinion, the correct solution is to decouple the URL from the file system location, and encode the history of URLs in a document's metadata. If you do that at HTML element granularity, you get actually persistent URLs, i.e. URIs. You won't get any of that from traditional document processing engines. Notion is a practical example where that part works well. I implemented a half-serious prototype using the module system to show how it could work in combination with source control.

@stablejoy
Copy link
Contributor

Im on a trip ten more days and I have little connection nor access to a laptop. I have mapped out the links but failed in making the js work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue site Improvements to the site infrastructure or content presentation
Projects
None yet
Development

No branches or pull requests

5 participants