aboutsummaryrefslogblamecommitdiff
path: root/scripts/assert-content.sh
blob: 08b256a60bcf2073e865f11917f7d629e3b1d1df (plain) (tree)
1
2
3
4
5
6
7
8
9


                   

                



                                           
                                                                                                                                                                                                                                                           









                                                      
 



                


                       
                     
                      
 
                                 
                                     
 

                              



                                   
                      
                             
                             
                            


                     













                                                    



                                             
           

                                                                   


            
                                                                 

 

                                              

 








                                                                                          

             
 
                      


                     
                      
                          
                         
                       

                               
 
                                                  
                                                 
                                                    
 

                                                      
          
    
 


                                                                         
          

    

                                         

                             
                                                   









                                                 


                                                  
                                                          




                                          
 





                                                      








                                                        

                      

    














                                                   






                                                      
 





                                                      








                                                           

                       

    

                                            
                                                
                                                              

                           
                                                             

            
 
                                  






                                              




                                                                                  
      


    
                         
                                                       
                          
                                
                                       
    

    




                                                             

                                                               
                                                     

    

                                                     
                                           

    

                                                         
                                                          

    

                                                             





                                                                   

    

                                 
                     
           

                            
                                  
                                 
                                
                                  








                                                            
      

      
 






                                                                               
 
              
#!/usr/bin/env bash
set -Eeuo pipefail

end="\033[0m"
red="\033[0;31m"
yellow="\033[0;33m"
red()    { echo -e "${red}${1}${end}"; }
yellow() { echo -e "${yellow}${1}${end}"; }

TRACKERS='-a udp://tracker.coppersurfer.tk:6969/announce -a udp://tracker.ccc.de:80/announce -a udp://tracker.publicbt.com:80 -a udp://tracker.istole.it:80 -a http://tracker.openbittorrent.com:80/announce -a http://tracker.ipv6tracker.org:80/announce'
AWK_S='
BEGIN {
  FRONTMATTER=0
}

/^---$/ {
  FRONTMATTER=!FRONTMATTER;
  getline; # strip empty line below end of frontmatter
  next;
}

FRONTMATTER==0 {
  print $0;
}
'

## Constant definitions

jekyll build --future
JSON='_site/site.json'

LANGS=(en pt fr eo) # jp zh es de
IGNORED_PAGES=(site.json sitemap.xml)

## Helper function definitions

slugify() {
  echo "${1}"                   | \
    tr '[:upper:]' '[:lower:]'  | \
    perl -ne 'tr/\000-\177//cd;
              s|/|-|g;
              s/[^\w\s-.]//g;
              s/^\s+|\s+$//g;
              s/[-\s.]+/-/g;
              print;'
}

contains-element() {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}

fail-attr() {
  ATTRIBUTE="${1}"
  URL="${2}"
  red "Undefined '${ATTRIBUTE}' for ${URL}." >&2
  exit 1
}

get-lang() {
  echo "${1}" | base64 --decode | jq -r .lang
}

get-url() {
  # Remove leading / to match more closely the filesystem hierarchy
  echo "${1}"  | base64 --decode | jq -r .url | sed 's_^/__'
}

get-date() {
  echo "${1}"  | base64 --decode | jq -r .date | awk '{print $1}'
}

get-x() {
  echo "${2}"  | base64 --decode | jq -r ".$1"
}

is-ignored() {
  URL="$1"
  EXTENSION="${URL##*.}"
  if contains-element "${URL}" "${IGNORED_PAGES[@]}" || [[ "$EXTENSION" == 'atom' ]]; then
    return 0
  else
    return 1
  fi
}

## Assertions

assert-frontmatter() {
  F="$1"
  DESIRED_LAYOUT="$2"
  PREFIX="${3:-}"
  EXTENSION="${4:-md}"
  LLANG="$(get-lang "$F")"
  REF="$(get-x ref "$F")"
  URL="$(get-url "$F")"
  LAYOUT="$(get-x layout "$F")"
  TITLE="$(get-x title "$F")"

  [[ -z "${LLANG}" ]] && fail-attr 'lang' "${URL}"
  [[ -z "${REF}" ]]  && fail-attr 'ref'  "${URL}"
  [[ -z "${TITLE}" ]]  && fail-attr 'title' "${URL}"

  if ! contains-element "${LLANG}" "${LANGS[@]}"; then
    red "Invalid lang '${LLANG}' in ${URL}." >&2
    exit 1
  fi

  if [[ "${DESIRED_LAYOUT}" != "${LAYOUT}" ]]; then
    red "Layout mismatch: expected '${DESIRED_LAYOUT}', got '${LAYOUT}'."
    red "Page: ${URL}."
    exit 1
  fi

  if [[ "$PREFIX" = '_podcasts/' ]]; then
    AUDIO="$(get-x audio "$F")"
    SLUG="$(get-x slug "$F")"

    [[ -z "$AUDIO" ]] && fail-attr 'audio' "${URL}"
    [[ -z "$SLUG" ]] && fail-attr 'slug' "${URL}"

    TITLE_SLUG="$(slugify "$TITLE")"
    if [[ "$SLUG" != "$TITLE_SLUG" ]]; then
      red "slug and title don't match."
      red "slug:       '$SLUG'"
      red "title slug: '$TITLE_SLUG'"
      exit 1
    fi

    for audiofmt in flac ogg; do
      DATE="$(get-date "$F")"
      URL_BASENAME="$(basename "$(get-url "$F")")"
      AUDIO="resources/podcasts/${DATE}-${SLUG}.$audiofmt"
      if [[ ! -f "$AUDIO" ]]; then
        red "Missing audio file '$AUDIO'."
        exit 1
      fi
    done

    OGG="resources/podcasts/$DATE-$SLUG.ogg"
    TORRENT="$OGG.torrent"
    WEBSEED="https://euandre.org/$OGG"
    if [ ! -f "$TORRENT" ]; then
      yellow "Missing torrent $TORRENT, generating..."
      # shellcheck disable=2086
      NOTES="$(awk "$AWK_S" "_podcasts/$DATE-$SLUG.md")"
      mktorrent $TRACKERS       \
                -f              \
                -v              \
                -d              \
                -c "$NOTES"     \
                -n "$TITLE.ogg" \
                -w "$WEBSEED"   \
                -o "$TORRENT"   \
                "$OGG"
    fi
  fi

  if [[ "$PREFIX" = '_screencasts/' ]]; then
    VIDEO="$(get-x video "$F")"
    SLUG="$(get-x slug "$F")"

    [[ -z "$VIDEO" ]] && fail-attr 'video' "${URL}"
    [[ -z "$SLUG" ]] && fail-attr 'slug' "${URL}"

    TITLE_SLUG="$(slugify "$TITLE")"
    if [[ "$SLUG" != "$TITLE_SLUG" ]]; then
      red "slug and title don't match."
      red "slug:       '$SLUG'"
      red "title slug: '$TITLE_SLUG'"
      exit 1
    fi

    DATE="$(get-date "$F")"
    URL_BASENAME="$(basename "$(get-url "$F")")"
    VIDEO="resources/screencasts/${DATE}-${SLUG}.webm"
    if [[ ! -f "$VIDEO" ]]; then
      red "Missing video file '$VIDEO'."
      exit 1
    fi

    WEBM="resources/screencasts/$DATE-$SLUG.webm"
    TORRENT="$WEBM.torrent"
    WEBSEED="https://euandre.org/$WEBM"
    if [ ! -f "$TORRENT" ]; then
      yellow "Missing torrent $TORRENT, generating..."
      # shellcheck disable=2086
      NOTES="$(awk "$AWK_S" "_screencasts/$DATE-$SLUG.md")"
      mktorrent $TRACKERS        \
                -f               \
                -v               \
                -d               \
                -c "$NOTES"      \
                -n "$TITLE.webm" \
                -w "$WEBSEED"    \
                -o "$TORRENT"    \
                "$WEBM"
    fi
  fi

  if [[ "$DESIRED_LAYOUT" != 'page' ]]; then
    DATE="$(get-date "$F")"
    URL_BASENAME="$(basename "$(get-url "$F")")"
    FILE="${PREFIX}${DATE}-${URL_BASENAME%.html}.${EXTENSION}"

    [[ -f "${FILE}" ]] || {
      red "date/filename mismatch: '${FILE}' does not exist."
      exit 1
    }

    if [[ "$LLANG" = 'en' ]]; then
      TITLE_SLUG="$(slugify "$TITLE")"
      if [[ "$TITLE_SLUG" != "$REF" ]]; then
        red "ref isn't the slug of the title."
        red "ref:        '$REF'"
        red "title slug: '$TITLE_SLUG'"
        exit 1
      fi
      DESIRED_FILE="${PREFIX}${DATE}-${TITLE_SLUG}.${EXTENSION}"
      if [[ ! -f "$DESIRED_FILE" ]]; then
        red "File can't be guessed from date+slug: '$DESIRED_FILE' does not exist"
        exit 1
      fi
    fi
  fi
}

echo Linting pages... >&2
for page in $(jq -r '.pages[] | @base64' "${JSON}"); do
  URL="$(get-url "$page")"
  if ! is-ignored "${URL}"; then
    assert-frontmatter "${page}" 'page'
  fi
done

echo Linting articles... >&2
for article in $(jq -r '.articles[] | @base64' "${JSON}"); do
  assert-frontmatter "$article" 'post' '_articles/'
done

echo Linting pastebins... >&2
for pastebin in $(jq -r '.pastebins[] | @base64' "${JSON}"); do
  assert-frontmatter "$pastebin" 'post' '_pastebins/'
done

echo Linting tils... >&2
for til in $(jq -r '.tils[] | @base64' "${JSON}"); do
  assert-frontmatter "$til" 'post' '_tils/'
done

echo Linting slides... >&2
for slide in $(jq -r '.slides[] | @base64' "${JSON}"); do
  assert-frontmatter "$slide" 'slides' '_slides/' 'slides'
done

echo Linting podcasts... >&2
for podcast in $(jq -r '.podcasts[] | @base64' "${JSON}"); do
  assert-frontmatter "$podcast" 'cast' '_podcasts/'
done

echo Linting screencasts... >&2
for screencast in $(jq -r '.screencasts[] | @base64' "${JSON}"); do
  assert-frontmatter "$screencast" 'cast' '_screencasts/'
done

echo Asserting unique refs... >&2
KNOWN_IDS=()
assert-unique-ref() {
  TYPE="$2"
  for page in $1; do
    URL="$(get-url "$page")"
    if ! is-ignored "${URL}"; then
      LLANG="$(get-lang "$page")"
      REF="$(get-x ref "$page")"
      ID="${TYPE}:${LLANG}:${REF}"

      if contains-element "${ID}" "${KNOWN_IDS[@]}"; then
        printf '%s\n' "${KNOWN_IDS[@]}"
        red "Duplicated lang:ref match: '${ID}'." >&2
        red "Page: ${URL}." >&2
        exit 1
      fi

      KNOWN_IDS+=("${ID}") # printf '%s\n' "${KNOWN_IDS[@]}"
    fi
  done
}

assert-unique-ref "$(jq -r       '.pages[] | @base64' "${JSON}")" 'page'
assert-unique-ref "$(jq -r    '.articles[] | @base64' "${JSON}")" 'article'
assert-unique-ref "$(jq -r        '.tils[] | @base64' "${JSON}")" 'til'
assert-unique-ref "$(jq -r   '.pastebins[] | @base64' "${JSON}")" 'pastebin'
assert-unique-ref "$(jq -r      '.slides[] | @base64' "${JSON}")" 'slides'
assert-unique-ref "$(jq -r    '.podcasts[] | @base64' "${JSON}")" 'podcasts'
assert-unique-ref "$(jq -r '.screencasts[] | @base64' "${JSON}")" 'screencasts'

echo Done. >&2