Compare commits

...

15 commits

Author SHA1 Message Date
Mirek Kratochvil 2eb5e608ac update gitlab CI to build using the LCSB runners from cloudy instance 2025-02-25 10:27:32 +01:00
Mirek Kratochvil 540055fb74 be nice and use metavars in --help 2025-01-29 20:48:41 +01:00
Mirek Kratochvil eeb49be54a Merge changes from the old gitlab 2025-01-29 20:39:57 +01:00
Mirek Kratochvil f0258edf00 fix dependencies, bump version, reformat 2025-01-29 20:38:00 +01:00
Mirek Kratochvil 859a65e61d extra metadata loading from a yaml file 2025-01-29 20:37:50 +01:00
Mirek Kratochvil 5fabdce6b8 fix entrypoint a bit 2025-01-29 20:36:41 +01:00
Mirek Kratochvil 7596161a26 add mount: URI scheme that allows folks to have somewhat relative links 2025-01-27 14:47:01 +01:00
Mirek Kratochvil e84caa8734 yarn went crazy, let's do npm only 2025-01-27 14:47:01 +01:00
Mirek Kratochvil a64aff0c1f some fixes 2025-01-27 14:47:01 +01:00
Mirek Kratochvil 0e710215b4 fix index URL appending with query strings and hashes, expose the index filename to the template 2025-01-27 14:47:01 +01:00
Mirek Kratochvil ebad2dcaa5 add mount: URI scheme that allows folks to have somewhat relative links 2025-01-27 14:42:53 +01:00
Mirek Kratochvil fc12ce3508 yarn went crazy, let's do npm only 2024-10-15 12:24:25 +02:00
Miroslav Kratochvil f12684b07e Merge branch 'mk-fix-url-appending' into 'master'
fix index URL appending with query strings and hashes, expose the index filename to the template

See merge request lcsb/sps/reploy!6
2024-10-14 16:31:05 +02:00
Mirek Kratochvil 541217b033 some fixes 2024-10-14 14:10:16 +02:00
Mirek Kratochvil 5d8872a5aa fix index URL appending with query strings and hashes, expose the index filename to the template 2024-02-21 15:00:27 +01:00
8 changed files with 157 additions and 75 deletions

View file

@ -1,13 +1,10 @@
image: docker:20.10.16 default:
image: repomanager.lcsb.uni.lu:9999/docker:27.4.0
variables: before_script:
DOCKER_DRIVER: overlay2 - unset DOCKER_HOST
DOCKER_TLS_CERTDIR: "" - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
tags:
services: - lcsb
- name: repomanager.lcsb.uni.lu:9999/library/docker:20.10.16-dind
command: ["--mtu=1458", "--registry-mirror", "https://repomanager.lcsb.uni.lu:9999"]
alias: docker
stages: stages:
- build - build
@ -15,12 +12,9 @@ stages:
build: build:
stage: build stage: build
script: script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build --pull --load -t $CI_REGISTRY_IMAGE:latest .
- docker build -t $CI_REGISTRY_IMAGE:latest .
- docker push $CI_REGISTRY_IMAGE:latest - docker push $CI_REGISTRY_IMAGE:latest
- docker tag $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker tag $CI_REGISTRY_IMAGE:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
rules: rules:
- if: '$CI_COMMIT_REF_NAME == "master"' - if: '$CI_COMMIT_REF_NAME == "master"'
tags:
- lcsb

View file

@ -17,7 +17,7 @@
FROM debian:trixie FROM debian:trixie
RUN apt -y update && apt -y install \ RUN apt -y update && apt -y install \
yarnpkg git git-lfs ssh rsync ghc cabal-install build-essential pkg-config zlib1g-dev jq yq \ npm nodejs git git-lfs ssh rsync ghc cabal-install build-essential pkg-config zlib1g-dev jq yq \
&& rm -fr /var/cache/apt && rm -fr /var/cache/apt
WORKDIR /opt/reploy WORKDIR /opt/reploy
@ -31,4 +31,4 @@ WORKDIR /data
COPY assets /data/assets/ COPY assets /data/assets/
COPY templates /data/templates/ COPY templates /data/templates/
COPY pages /data/pages/ COPY pages /data/pages/
ENTRYPOINT ["/root/.cabal/bin/reploy"] ENTRYPOINT ["/root/.local/bin/reploy"]

View file

@ -20,9 +20,7 @@
-- | Separated-out main types of the deployment scriptage. -- | Separated-out main types of the deployment scriptage.
module Types where module Types where
import AesonUtils
import Control.Monad.Trans.State.Lazy import Control.Monad.Trans.State.Lazy
import qualified Data.ByteString.UTF8
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.Yaml as Y import qualified Data.Yaml as Y
@ -40,6 +38,14 @@ data PageInfo = PageInfo
makeLenses ''PageInfo makeLenses ''PageInfo
-- | Information about where to source all extra metadata
data MetaSpec
= MetaSpecInline String
| MetaSpecFile FilePath
deriving (Show)
makeLenses ''MetaSpec
-- | Complete internal state of the deployment process that holds all data -- | Complete internal state of the deployment process that holds all data
data SiteState = SiteState data SiteState = SiteState
{ _pages :: M.Map FilePath PageInfo -- ^ Map of page mounts to `PageInfo` { _pages :: M.Map FilePath PageInfo -- ^ Map of page mounts to `PageInfo`
@ -62,6 +68,7 @@ data SiteState = SiteState
, _tagTemplate :: FilePath -- ^ Name of the template for category pages , _tagTemplate :: FilePath -- ^ Name of the template for category pages
, _listTemplate :: FilePath -- ^ Name of the template for listing pages , _listTemplate :: FilePath -- ^ Name of the template for listing pages
, _extraMeta :: Y.Value -- ^ Extra metadata added to rendering of all templates , _extraMeta :: Y.Value -- ^ Extra metadata added to rendering of all templates
, _extraMetaSpec :: [MetaSpec] -- ^ sources for the extra metadata
, _metadataSuffix :: FilePath -- ^ File suffix to search for a extra metadata (e.g., if the suffix is ".extra", the extra metadata for file "page.md" will be looked for in "page.md.extra"). These are best autogenerated with a script that sources the data from git or so. , _metadataSuffix :: FilePath -- ^ File suffix to search for a extra metadata (e.g., if the suffix is ".extra", the extra metadata for file "page.md" will be looked for in "page.md.extra"). These are best autogenerated with a script that sources the data from git or so.
, _indexFile :: FilePath -- ^ Name of the "index" files to be generated. , _indexFile :: FilePath -- ^ Name of the "index" files to be generated.
, _urlBase :: FilePath -- ^ "Root route" to prepend to all absolute links. , _urlBase :: FilePath -- ^ "Root route" to prepend to all absolute links.
@ -81,6 +88,7 @@ siteOptions' = do
strOption strOption
$ long "output" $ long "output"
<> short 'd' <> short 'd'
<> metavar "OUTDIR"
<> help "Directory to render the site to" <> help "Directory to render the site to"
<> value "_site" <> value "_site"
<> showDefault <> showDefault
@ -88,28 +96,33 @@ siteOptions' = do
Just Just
<$> (strOption <$> (strOption
$ long "search-data-output" $ long "search-data-output"
<> metavar "JSON"
<> help "Output JSON with searchable page data to this file") <> help "Output JSON with searchable page data to this file")
<|> pure Nothing <|> pure Nothing
_assetDirs <- _assetDirs <-
many . strOption many . strOption
$ long "assets" $ long "assets"
<> short 'a' <> short 'a'
<> metavar "DIR"
<> help <> help
"Assets directory to be copied verbatim (possibly multiple paths)" "Assets directory to be copied verbatim (possibly multiple paths)"
_sourceDirs <- _sourceDirs <-
many . strOption many . strOption
$ long "source-directory" $ long "source-directory"
<> short 's' <> short 's'
<> metavar "DIR"
<> help <> help
"Path to the directory with source data (possibly multiple paths)" "Path to the directory with source data (possibly multiple paths)"
_notSourceDirs <- _notSourceDirs <-
many . strOption many . strOption
$ long "exclude-source-directory" $ long "exclude-source-directory"
<> metavar "EXCLUDE"
<> help <> help
"Names of subdirectories of the sources that should never be used for sourcing pages (possibly multiple directory names)" "Names of subdirectories of the sources that should never be used for sourcing pages (possibly multiple directory names)"
_tagMetaFile <- _tagMetaFile <-
strOption strOption
$ long "tag-metadata-file" $ long "tag-metadata-file"
<> metavar "FILENAME"
<> help "Name of files with tag metadata" <> help "Name of files with tag metadata"
<> value "tag-metadata.yml" <> value "tag-metadata.yml"
<> showDefault <> showDefault
@ -117,11 +130,13 @@ siteOptions' = do
many . strOption many . strOption
$ long "template-directory" $ long "template-directory"
<> short 't' <> short 't'
<> metavar "DIR"
<> help <> help
"Path to the directory with templates (possibly multiple paths)" "Path to the directory with templates (possibly multiple paths)"
_defaultTemplate <- _defaultTemplate <-
strOption strOption
$ long "default-template" $ long "default-template"
<> metavar "FILENAME"
<> help <> help
"Default template to use for stuff (as found in templates directory)" "Default template to use for stuff (as found in templates directory)"
<> value "default.html" <> value "default.html"
@ -129,53 +144,61 @@ siteOptions' = do
_redirectTemplate <- _redirectTemplate <-
strOption strOption
$ long "redirect-template" $ long "redirect-template"
<> metavar "FILENAME"
<> help "Template for making redirect pages" <> help "Template for making redirect pages"
<> value "redirect.html" <> value "redirect.html"
<> showDefault <> showDefault
_tagTemplate <- _tagTemplate <-
strOption strOption
$ long "tag-template" $ long "tag-template"
<> metavar "FILENAME"
<> help "Template for making category view pages" <> help "Template for making category view pages"
<> value "tag.html" <> value "tag.html"
<> showDefault <> showDefault
_listTemplate <- _listTemplate <-
strOption strOption
$ long "list-template" $ long "list-template"
<> metavar "FILENAME"
<> help "Template for making tag-listing pages" <> help "Template for making tag-listing pages"
<> value "list.html" <> value "list.html"
<> showDefault <> showDefault
_metadataSuffix <- _metadataSuffix <-
strOption strOption
$ long "metadata-suffix" $ long "metadata-suffix"
<> metavar "SUFFIX"
<> help <> help
"Suffix for YAML files with base metadata for each markdown page. Metadata from files override the extra metadata specified on commandline, but are overridden by metadata specified directly in the markdown header of the pages." "Suffix for YAML files with base metadata for each markdown page. Metadata from files override the extra metadata specified on commandline, but are overridden by metadata specified directly in the markdown header of the pages."
<> value ".metadata.yml" <> value ".metadata.yml"
<> showDefault <> showDefault
_extraMeta <- _extraMetaSpec <-
let processKeyVal :: String -> Y.Value many
processKeyVal opt = $ asum
case Y.decodeEither' $ Data.ByteString.UTF8.fromString opt of [ fmap MetaSpecInline . strOption
Right v -> v
Left err ->
error
$ "cannot parse YAML in --extra-metadata: "
++ Y.prettyPrintParseException err
in fmap (foldl objMerge Y.Null . map processKeyVal) . many . strOption
$ long "extra-metadata" $ long "extra-metadata"
<> short 'e' <> short 'e'
<> metavar "YAML"
<> help <> help
"Extra metadata to add to pages rendering in YAML format. May be specified multiple times." "Extra metadata to add to pages rendering in YAML format. May be specified multiple times."
, fmap MetaSpecFile . strOption
$ long "extra-metadata-file"
<> short 'E'
<> metavar "FILE"
<> help
"Extra metadata to add to pages rendering, loaded from a YAML file. May be specified multiple times."
]
_urlBase <- _urlBase <-
strOption strOption
$ long "url-base" $ long "url-base"
<> short 'u' <> short 'u'
<> metavar "URL"
<> help "Base absolute URL" <> help "Base absolute URL"
<> value "/" <> value "/"
<> showDefault <> showDefault
_indexFile <- _indexFile <-
strOption strOption
$ long "index-filename" $ long "index-filename"
<> help "Base absolute URL" <> metavar "FILENAME"
<> help "Directory index to use for page output."
<> value "index.html" <> value "index.html"
<> showDefault <> showDefault
_appendUrlIndex <- _appendUrlIndex <-
@ -199,6 +222,7 @@ siteOptions' = do
, _installs = S.empty , _installs = S.empty
, _targets = S.empty , _targets = S.empty
, _templates = M.empty , _templates = M.empty
, _extraMeta = Y.Null
, .. , ..
} }

View file

@ -10,3 +10,5 @@ timestamp: null
# Well hello there # Well hello there
This site is empty, you can start populating it! This site is empty, you can start populating it!
[link test](/?link-test)

View file

@ -1,18 +1,24 @@
cabal-version: 3.0 cabal-version: 3.0
name: reploy name: reploy
synopsis: Straightforward static all-in-one website builder synopsis: Straightforward static all-in-one website builder
category: Web category: Web
version: 0.3.2.0 version: 0.3.3.0
build-type: Simple build-type: Simple
license: Apache-2.0 license: Apache-2.0
license-file: LICENSE license-file: LICENSE
executable reploy executable reploy
main-is: reploy.hs main-is: reploy.hs
other-modules: Types, AesonUtils, Utils, Tags, FormatOpts other-modules:
build-depends: base == 4.* AesonUtils
, aeson ^>= 2.1 FormatOpts
Tags
Types
Utils
build-depends:
, aeson ^>=2.1 || ^>=2.2
, base >=4 && <5
, bytestring , bytestring
, containers , containers
, data-default , data-default
@ -34,5 +40,6 @@ executable reploy
, transformers , transformers
, utf8-string , utf8-string
, yaml , yaml
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wunused-imports ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wunused-imports
default-language: Haskell2010 default-language: Haskell2010

View file

@ -26,6 +26,7 @@ import qualified Data.Aeson as AE
import qualified Data.Aeson.Key as K import qualified Data.Aeson.Key as K
import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.KeyMap as KM
import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.UTF8
import Data.Digest.Pure.SHA (sha256, showDigest) import Data.Digest.Pure.SHA (sha256, showDigest)
import Data.Foldable (traverse_) import Data.Foldable (traverse_)
import Data.List (inits, nub, sort) import Data.List (inits, nub, sort)
@ -46,6 +47,7 @@ import System.FilePath
( isAbsolute ( isAbsolute
, joinPath , joinPath
, splitFileName , splitFileName
, splitPath
, takeDirectory , takeDirectory
, takeFileName , takeFileName
) )
@ -175,14 +177,25 @@ rootedLink :: FilePath -> Site FilePath
rootedLink = (<*>) rootedLink' . pure rootedLink = (<*>) rootedLink' . pure
-- | Process a single link pointing out from a page. -- | Process a single link pointing out from a page.
processLink :: FilePath -> FilePath -> Site String processLink :: FilePath -> FilePath -> FilePath -> Site String
processLink base l = processLink base mount l
if any (l `hasUriScheme`) ["http", "https", "ftp", "mailto"] | any (l `hasUriScheme`) ["http", "https", "ftp", "mailto"] || take 1 l == "#" =
|| take 1 l == "#" pure l
then pure l | isAbsolute l =
else if isAbsolute l let (path, rest) = break (`elem` ['?', '#']) l
then rootedPageLink l in (<> rest) <$> rootedPageLink path
else installFile (base </> l) >>= rootedLink | l `hasUriScheme` "mount" =
let (path, rest) = break (`elem` ['?', '#']) $ drop 6 l
mountpath =
joinPath . reverse
$ foldl interpretPath (reverse $ splitPath mount) (splitPath path)
interpretPath m x
| x `elem` ["..", "../"] = drop 1 m
| x `elem` [".", "./"] = m
| x == "/" = ["/"]
| otherwise = x : m
in (<> rest) <$> rootedPageLink mountpath
| otherwise = installFile (base </> l) >>= rootedLink
-- | Conjure a function that finds a displayable name for a page at a particular mount. -- | Conjure a function that finds a displayable name for a page at a particular mount.
pageName' :: Site (FilePath -> String) pageName' :: Site (FilePath -> String)
@ -208,6 +221,8 @@ addGlobalMeta meta = do
rt <- rootedLink' rt <- rootedLink'
rtp <- rootedPageLink' rtp <- rootedPageLink'
pn <- pageName' pn <- pageName'
aui <- use appendUrlIndex
ifi <- use indexFile
Y.Object m <- (`objMerge` meta) <$> use extraMeta Y.Object m <- (`objMerge` meta) <$> use extraMeta
let l = map (\(k, v) -> (K.toText k, Mu.toMustache v)) $ KM.toList m let l = map (\(k, v) -> (K.toText k, Mu.toMustache v)) $ KM.toList m
pure . Mu.object pure . Mu.object
@ -217,6 +232,9 @@ addGlobalMeta meta = do
, ("pageLink", Mu.overText $ T.pack . rtp . T.unpack) , ("pageLink", Mu.overText $ T.pack . rtp . T.unpack)
, ("pageName", Mu.overText $ T.pack . pn . T.unpack) , ("pageName", Mu.overText $ T.pack . pn . T.unpack)
] ]
++ (if aui
then [("indexFile", Mu.toMustache $ T.pack ifi)]
else [])
-- | Get the expected timestamp file for a given filepath -- | Get the expected timestamp file for a given filepath
metadataFile :: FilePath -> Site FilePath metadataFile :: FilePath -> Site FilePath
@ -284,7 +302,8 @@ installPage mount pi = do
templ <- use $ templates . to (M.! fromString tname) templ <- use $ templates . to (M.! fromString tname)
file <- indexFilename mount file <- indexFilename mount
fixedUrlDoc <- fixedUrlDoc <-
walkURLs (processLink $ pi ^. pagePath . to takeDirectory) $ pi ^. pageDoc walkURLs (processLink (pi ^. pagePath . to takeDirectory) mount)
$ pi ^. pageDoc
checkTarget file checkTarget file
body <- body <-
io . runIOorExplode io . runIOorExplode
@ -493,10 +512,37 @@ renderSearchData = use searchDataOut >>= traverse_ go
putStrLn $ "S -> " ++ out putStrLn $ "S -> " ++ out
AE.encodeFile out $ Y.array (concat ps) AE.encodeFile out $ Y.array (concat ps)
-- | Souce extra metadata accordingly to the metadata specifications
sourceExtraMetadata :: Site ()
sourceExtraMetadata = do
use extraMetaSpec
>>= fmap (foldl objMerge Y.Null) . traverse loadSpec
>>= assign extraMeta
where
loadSpec :: MetaSpec -> Site Y.Value
loadSpec (MetaSpecInline yaml) =
case Y.decodeEither' $ Data.ByteString.UTF8.fromString yaml of
Right v -> pure v
Left err ->
error
$ "cannot parse extra metadata from inline YAML: "
++ Y.prettyPrintParseException err
loadSpec (MetaSpecFile path) = do
res <- io $ Y.decodeFileEither path
case res of
Right v -> pure v
Left err ->
error
$ "cannot load YAML metadata from "
++ path
++ ": "
++ Y.prettyPrintParseException err
-- | Build the whole site. -- | Build the whole site.
main = do main = do
init <- Options.Applicative.execParser siteOptions init <- Options.Applicative.execParser siteOptions
flip runStateT init $ do flip runStateT init $ do
sourceExtraMetadata
installAssets installAssets
use sourceDirs >>= traverse sourcePages use sourceDirs >>= traverse sourcePages
use sourceDirs >>= traverse sourceTagMeta use sourceDirs >>= traverse sourceTagMeta

View file

@ -1,4 +1,10 @@
<header> <header>
{{?menu_items}}Menu:
<ul>
{{#menu_items}}<li>{{.}}</li>{{/menu_items}}
</ul>
{{/menu_items}}
Navigation: Navigation:
<ul> <ul>
<li><a href="{{#pageLink}}/{{/pageLink}}">{{#pageName}}/{{/pageName}}</a></li> <li><a href="{{#pageLink}}/{{/pageLink}}">{{#pageName}}/{{/pageName}}</a></li>

3
test.yml Normal file
View file

@ -0,0 +1,3 @@
menu_items:
- item1
- item2