feat: add dead-link checker to build pipeline
This commit is contained in:
8
Makefile
8
Makefile
@@ -61,9 +61,6 @@ server-v:
|
||||
.PHONY: build
|
||||
build: dist
|
||||
@$(LEKTOR) build --output-path ../bin --buildstate-path build-state -f ENABLE_PDF_EXPORT
|
||||
@echo
|
||||
@echo 'Checking dead links ...'
|
||||
@python3 extras/find-dead-links.py 'data/development'
|
||||
|
||||
.PHONY: deploy
|
||||
deploy:
|
||||
@@ -90,6 +87,11 @@ find-links:
|
||||
|| echo 'nothing found.'
|
||||
@echo
|
||||
|
||||
.PHONY: find-dead-links
|
||||
find-dead-links:
|
||||
@echo 'Checking dead links ...'
|
||||
@python3 extras/find-dead-links.py 'data/development'
|
||||
|
||||
.PHONY: find-yield
|
||||
find-yield:
|
||||
@echo
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from lektor.types import Type
|
||||
from typing import TYPE_CHECKING, List, Optional, Any
|
||||
from typing import TYPE_CHECKING, List, Optional, Any, Iterator, Tuple
|
||||
if TYPE_CHECKING:
|
||||
from lektor.builder import Builder
|
||||
from lektor.db import Record
|
||||
from lektor.types.base import RawValue
|
||||
from .utils import replaceFractions
|
||||
from .settings import IngredientConfig
|
||||
from .utils import replaceFractions
|
||||
|
||||
|
||||
class IngredientEntry:
|
||||
@@ -111,3 +112,34 @@ class IngredientsListType(Type):
|
||||
|
||||
def value_from_raw(self, raw: 'RawValue') -> IngredientsDescriptor:
|
||||
return IngredientsDescriptor(raw.value or None)
|
||||
|
||||
|
||||
##############################
|
||||
# Check cross-recipe links #
|
||||
##############################
|
||||
|
||||
def _detect_atref_urls(recipe: 'Record') -> Iterator[str]:
|
||||
''' Internal method to iterate over recipe-links in ingredient notes. '''
|
||||
for ing in recipe['ingredients']:
|
||||
if ing.isIngredient and '@' in ing.note:
|
||||
for part in ing.note.split():
|
||||
if part.startswith('@../'):
|
||||
yield part[4:].rstrip('/')
|
||||
|
||||
|
||||
def check_dead_links(builder: 'Builder') -> Iterator[Tuple['Record', str]]:
|
||||
'''
|
||||
Iterate over all recipes and all ingredients notes.
|
||||
If a note contains a recipe link, check if the link is a valid target.
|
||||
If not, print to log but continue building (soft error).
|
||||
|
||||
returns: [recipe, ref-link]
|
||||
'''
|
||||
for alt in builder.pad.config.iter_alternatives():
|
||||
# funny enough, .query('/recipes') does not populate ingredients
|
||||
all_recipes = builder.pad.get('/recipes', alt=alt).children
|
||||
all_ids = set(x['_slug'] for x in all_recipes)
|
||||
for recipe in all_recipes:
|
||||
for ref in _detect_atref_urls(recipe):
|
||||
if ref not in all_ids:
|
||||
yield recipe, f'@../{ref}/'
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from lektor.databags import Databags
|
||||
from lektor.db import Page # isinstance
|
||||
from lektor.pluginsystem import Plugin
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any, List, Dict, Tuple, Set, Iterator
|
||||
if TYPE_CHECKING:
|
||||
@@ -11,8 +12,9 @@ if TYPE_CHECKING:
|
||||
from .durationcluster import (
|
||||
int_to_cluster, cluster_as_str, human_readable_duration
|
||||
)
|
||||
from .ingredients import IngredientsListType
|
||||
from .ingredients import IngredientsListType, check_dead_links
|
||||
from .latex import TexSources, raw_text_to_tex, html_to_tex
|
||||
from .log import Log
|
||||
from .settings import Settings
|
||||
from .utils import fillupText, replace_atref_urls, cover_image, noUmlauts
|
||||
|
||||
@@ -60,6 +62,14 @@ class MainPlugin(Plugin):
|
||||
# or else latex fails because it cannot find referenced images
|
||||
TexSources.build(builder)
|
||||
|
||||
def on_after_prune(self, builder: 'Builder', **extra: Any) -> None:
|
||||
root = self.env.root_path # type: str
|
||||
with Log.group('check dead links', builder):
|
||||
for recipe, link in check_dead_links(builder):
|
||||
Log.error('dead-link: {} ({})'.format(
|
||||
os.path.relpath(recipe.source_filename or '', root),
|
||||
Log.Style.red(link)))
|
||||
|
||||
##############
|
||||
# Duration #
|
||||
##############
|
||||
|
||||
Reference in New Issue
Block a user