Commit da5e58cb authored by Dario Pinto's avatar Dario Pinto
Browse files

Add blog article meta data display, prepare blog generation

parent c7f29276
let get_value field = List.hd (List.rev (String.split_on_char ':' field))
let post_header t a d c tg =
let title = get_value t in
let authors = get_value a in
let date = get_value d in
let category = get_value c in
let tags = get_value tg in
{|<h1 id="page-title">%s</h1>
<div class="row">
<div class="col-lg-3">
<img src="/blog/assets/img/icon_person.svg" style="max-width:1em"/>
Authors: %s
<div class="col-lg-3">
<img src="/blog/assets/img/icon_calendar.svg" style="max-width:1em"/>
Date: %s
<div class="col-lg-3">
<img src="/blog/assets/img/icon_category.svg" style="max-width:1em"/>
Category: %s
<div class="col-lg-3">
<img src="/blog/assets/img/icon_tags.svg" style="max-width:1em"/>
Tags: %s
<hr class="featurette-divider"/>
title authors date category tags
<svg xmlns="" width="16" height="16" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
\ No newline at end of file
<svg xmlns="" width="16" height="16" fill="currentColor" class="bi bi-stack" viewBox="0 0 16 16">
<path d="m14.12 10.163 1.715.858c. 0 .534L8.267 15.34a.598.598 0 0 1-.534 0L.165 11.555a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0l5.317-2.66zM7.733.063a.598.598 0 0 1 .534 0l7.568 3.784a.3.3 0 0 1 0 .535L8.267 8.165a.598.598 0 0 1-.534 0L.165 4.382a.299.299 0 0 1 0-.535L7.733.063z"/>
<path d="m14.12 6.576 1.715.858c. 0 .534l-7.568 3.784a.598.598 0 0 1-.534 0L.165 7.968a.299.299 0 0 1 0-.534l1.716-.858 5.317 2.659c.505.252 1.1.252 1.604 0l5.317-2.659z"/>
\ No newline at end of file
<svg xmlns="" width="16" height="16" fill="currentColor" class="bi bi-person" viewBox="0 0 16 16">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10z"/>
\ No newline at end of file
<svg xmlns="" width="16" height="16" fill="currentColor" class="bi bi-tags-fill" viewBox="0 0 16 16">
<path d="M2 2a1 1 0 0 1 1-1h4.586a1 1 0 0 1 .707.293l7 7a1 1 0 0 1 0 1.414l-4.586 4.586a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 2 6.586V2zm3.5 4a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
<path d="M1.293 7.793A1 1 0 0 1 1 7.086V2a1 1 0 0 0-1 1v4.586a1 1 0 0 0 .293.707l7 7a1 1 0 0 0 1.414 0l.043-.043-7.457-7.457z"/>
\ No newline at end of file
title: opam 2.1.0 is released!
authors: rjbou
date: 2021-08-05
category: tooling
tags: opam
<div class="row">
<div class="col-lg-12" align="center">
<img src="/blog/assets/img/logo_opam_blue.png" class="img-fluid rounded"/>
<br />
We are happy to announce the release of opam 2.1.0.
Many new features made it in (see the <a href="">pre-release changelogs</a> or <a href="">release notes</a> for the details), but here are a few highlights.
<h2 class="page-subtitle">What's new in opam 2.1?</h2>
- Integration of system dependencies (formerly the opam-depext plugin), increasing their reliability as it integrates the solving step
- Creation of lock files for reproducible installations (formerly the opam-lock plugin)
- Switch invariants, replacing the "base packages" in opam 2.0 and allowing for easier compiler upgrades
- Improved options configuration (see the new ``option`` and expanded ``var`` sub-commands)
- CLI versioning, allowing cleaner deprecations for opam now and also improvements to semantics in future without breaking backwards-compatibility
- opam root readability by newer and older versions, even if the format changed
- Performance improvements to opam-update, conflict messages, and many other areas
<h3>Seamless integration of System dependencies handling (a.k.a. "depexts")</h3>
opam has long included the ability to install system dependencies automatically via the <a href="">depext plugin</a>. This plugin has been promoted to a native feature of opam 2.1.0 onwards, giving the following benefits:
You no longer have to remember to run <code>opam depext</code>, opam always checks depexts (there are options to disable this or automate it for CI use). Installation of an opam package in a CI system is now as easy as <code>opam install .</code>, without having to do the dance of <code>opam pin add -n/depext/install</code>. Just one command now for the common case!
The solver is only called once, which both saves time and also stabilises the behaviour of opam in cases where the solver result is not stable. It was possible to get one package solution for the <code>opam depext</code> stage and a different solution for the <code>opam install</code> stage, resulting in some depexts missing.
opam now has full knowledge of depexts, which means that packages can be automatically selected based on whether a system package is already installed. For example, if you have neither MariaDB nor MySQL dev libraries installed, <code>opam install mysql</code> will offer to install <code>conf-mysql</code> and <code>mysql</code>, but if you have the MariaDB dev libraries installed, opam will offer to install <code>conf-mariadb</code> and <code>mysql</code>.
Hint: You can set <code>OPAMCONFIRMLEVEL=unsafe-yes</code> or <code>--confirm-level=unsafe-yes</code> to launch non interactive system package commands.
<h3>opam lock files and reproducibility</h3>
When opam was first released, it had the mission of gathering together scattered OCaml source code to build a <a href="">community repository</a>. As time marches on, the size of the opam repository has grown tremendously, to over 3000 unique packages with over 19500 unique versions. opam looks at all these packages and is designed to solve for the best constraints for a given package, so that your project can keep up with releases of your dependencies.
While this works well for libraries, we need a different strategy for projects that need to test and ship using a fixed set of dependencies. To satisfy this use-case, opam 2.0.0 shipped with support for <em>using</em> <code>project.opam.locked</code> files. These are normal opam files but with exact versions of dependencies. The lock file can be used as simply as <code>opam install . --locked</code> to have a reproducible package installation.
With opam 2.1.0, the creation of lock files is also now integrated into the client:
<ul><li><code>opam lock</code> will create a <code>.locked</code> file for your current switch and project, that you can check into the repository.</li><li><code>opam switch create . --locked</code> can be used by users to reproduce your dependencies in a fresh switch.</li></ul>
This lets a project simultaneously keep up with the latest dependencies (without lock files) while providing a stricter set for projects that need it (with lock files).
<em>Hint: You can export the full configuration of a switch with <code>opam switch export</code> new options, <code>--full</code> to have all packages metadata included, and <code>--freeze</code> to freeze all VCS to their current commit.</em>
<h3>Switch invariants</h3>
In opam 2.0, when a switch is created the packages selected are put into the “base” of the switch. These packages are not normally considered for upgrade, in order to ease pressure on opam's solver. This was a much bigger concern early on in opam 2.0's development, but is less of a problem with the default mccs solver.
However, it's a problem for system compilers. opam would detect that your system compiler version had changed, but be unable to upgrade the ocaml-system package unless you went through a slightly convoluted process with <code>--unlock-base</code>.
In opam 2.1, base packages have been replaced by switch invariants. The switch invariant is a package formula which must be satisfied on every upgrade and install. All existing switches' base packages could just be expressed as <code>package1 &amp; package2 &amp; package3</code> etc. but opam 2.1 recognises many existing patterns and simplifies them, so in most cases the invariant will be <code>"ocaml-base-compiler" {= "4.11.1"}</code>, etc. This means that <code>opam switch create my_switch ocaml-system</code> now creates a <em>switch invariant</em> of <code>"ocaml-system"</code> rather than a specific version of the <code>ocaml-system</code> package. If your system OCaml package is updated, <code>opam upgrade</code> will seamlessly switch to the new package.
This also allows you to have switches which automatically install new point releases of OCaml. For example:
<pre class="wp-block-preformatted">opam switch create ocaml-4.11 --formula='"ocaml-base-compiler" {>= "4.11.0" & < "4.12.0~"}' --repos=old=git+
opam install utop</pre>
Creates a switch with OCaml 4.11.0 (the <code>--repos=</code> was just to select a version of opam-repository from before 4.11.1 was released). Now issue:
<pre class="wp-block-preformatted">opam repo set-url old git+
opam upgrade</pre>
and opam 2.1 will automatically offer to upgrade OCaml 4.11.1 along with a rebuild of the switch. There's not yet a clean CLI for specifying the formula, but we intend to iterate further on this with future opam releases so that there is an easier way of saying “install OCaml 4.11.x”.
<em>Hint: You can set up a default invariant that will apply for all new switches, via a specific <code>opamrc</code>. The default one is <code>ocaml &gt;= 4.05.0</code></em>
<h3>Configuring opam from the command-line</h3>
Configuring opam is not a simple task: you need to use an <code>opamrc</code> at init stage, or hack global/switch config file, or use <code>opam config var</code> for additional variables. To ease that step, and permit a more consistent opam config tweaking, a new command was added : <code>opam option</code>.
For example:
<code>opam option download-jobs</code> gives the global <code>download-jobs</code> value (as it exists only in global configuration)
<code>opam option jobs=6 --global</code> will set the number of parallel build jobs opam is allowed to run (along with the associated <code>jobs</code> variable)
<code>opam option depext-run-commands=false</code> disables the use of <code>sudo</code> for handling system dependencies; it will be replaced by a prompt to run the installation commands
<code>opam option depext-bypass=m4 --global</code> bypass <code>m4</code> system package check globally, while <code>opam option depext-bypass=m4 --switch myswitch</code> will only bypass it in the selected switch
The command <code>opam var</code> is extended with the same format, acting on switch and global variables.
<em>Hint: to revert your changes use <code>opam option &lt;field&gt;=</code>, it will take its default value.</em>
<h3>CLI Versioning</h3>
A new <code>--cli</code> switch was added to the first beta release, but it's only now that it's being widely used. opam is a complex enough system that sometimes bug fixes need to change the semantics of some commands. For example:
<code>opam show --file</code> needed to change behaviour
The addition of new controls for setting global variables means that the <code>opam config</code> was becoming cluttered and some things want to move to <code>opam var</code>
<code>opam switch install 4.11.1</code> still works in opam 2.0, but it's really an OPAM 1.2.2 syntax.
Changing the CLI is exceptionally painful since it can break scripts and tools which themselves need to drive <code>opam</code>. CLI versioning is our attempt to solve this. The feature is inspired by the <code>(lang dune ...)</code> stanza in <code>dune-project</code> files which has allowed the Dune project to rename variables and alter semantics without requiring every single package using Dune to upgrade their <code>dune</code> files on each release.
Now you can specify which version of opam you expected the command to be run against. In day-to-day use of opam at the terminal, you wouldn't specify it, and you'll get the latest version of the CLI. For example: <code>opam var --global</code> is the same as <code>opam var --cli=2.1 --global</code>. However, if you issue <code>opam var --cli=2.0 --global</code>, you will told that <code>--global</code> was added in 2.1 and so is not available to you. You can see similar things with the renaming of <code>opam upgrade --unlock-base</code> to <code>opam upgrade --update-invariant</code>.
The intention is that <code>--cli</code> should be used in scripts, user guides (e.g. blog posts), and in software which calls opam. The only decision you have to take is the <em>oldest</em> version of opam which you need to support. If your script is using a new opam 2.1 feature (for example <code>opam switch create --formula=</code>) then you simply don't support opam 2.0. If you need to support opam 2.0, then you can't use <code>--formula</code> and should use <code>--packages</code> instead. opam 2.0 does not have the <code>--cli</code> option, so for opam 2.0 instead of <code>--cli=2.0</code> you should set the environment variable <code>OPAMCLI</code> to <code>2.0</code>. As with <em>all</em> opam command line switches, <code>OPAMCLI</code> is simply the equivalent of <code>--cli</code> which opam 2.1 will pick-up but opam 2.0 will quietly ignore (and, as with other options, the command line takes precedence over the environment).
Note that opam 2.1 sets <code>OPAMCLI=2.0</code> when building packages, so on the rare instances where you need to use the <code>opam</code> command in a <em>package</em> <code>build:</code> command (or in your build system), you <em>must</em> specify <code>--cli=2.1</code> if you're using new features.
Since 2.1.0~rc2, CLI versioning applies to opam environment variables. The previous behavior was to ignore unknown or wrongly set environment variable, while now you will have a warning to let you know that the environment variable won't be handled by this version of opam.
To ensure not breaking compatibility of some widely used deprecated options, a <em>default</em> CLI is introduced: when no CLI is specified, those deprecated options are accepted. It concerns <code>opam exec</code> and <code>opam var</code> subcommands.
There's even more detail on this feature <a href="">in our wiki</a>. We're hoping that this feature will make it much easier in future releases for opam to make required changes and improvements to the CLI without breaking existing set-ups and tools.
<em>Note: For opam libraries users, since 2.1 environment variable are no more loaded by the libraries, only by opam client. You need to load them explicitly.</em>
<h3>opam root portability</h3>
opam root format changes during opam life-cycle, new field are added or removed, new files are added ; an older opam version sometimes can no longer read an upgraded or newly created opam root. opam root format has been updated to allow new versions of opam to indicate that the root may still be read by older versions of the opam libraries. A plugin compiled against the 2.0.9 opam libraries will therefore be able to read information about an opam 2.1 root (plugins and tools compiled against 2.0.8 are unable to load opam 2.1.0 roots). It is a <em>read-only</em> best effort access, any attempt to modify the opam root fails.
<em>Hint: for opam libraries users, you can safely load states with <a href=""><code>OpamStateConfig</code></a> load functions.</em>
<strong>Tremendous thanks to all involved people, who've developed, tested &amp; retested, helped with issue reports, comments, feedback...</strong>
<h2 class="page-subtitle">Try it!</h2>
In case you plan a possible rollback, you may want to first backup your <code>~/.opam</code> directory.
The upgrade instructions are unchanged:
Either from binaries: run <code>bash -c "sh &lt;(curl -fsSL --version 2.1.0"</code> or download manually from <a href="">the Github "Releases" page</a> to your PATH.
Or from source, manually: see the instructions in the <a href="">README</a>.
You should then run:
<pre class="wp-block-preformatted">opam init --reinit -ni</pre>
(public_name server)
(modules content server template)
(modules content server template blog blog_content)
(libraries dream omd))
......@@ -17,3 +17,12 @@
(run ocaml-crunch -m plain content -o %{target}))))
(source_tree content/blog/assets))
(run ocaml-crunch -m plain content/blog/assets -o %{target}))))
......@@ -6,21 +6,44 @@ let asset_loader _root path _request =
| None -> Dream.empty `Not_Found
| Some asset -> Dream.respond asset
let blog_asset_loader _root path _request =
match path with
| None -> Dream.empty `Not_Found
| Some asset -> Dream.respond asset
let page path =
match (path ^ ".md") with
| None -> None
| Some page -> Some (Omd.of_string page |> Omd.to_html)
let article path =
match ("blog/" ^ path ^ ".md") with
| None -> None
| Some blog_article -> (
match String.split_on_char '\n' blog_article with
| title :: authors :: date :: category :: tags :: article ->
let header = Blog.post_header title authors date category tags in
Some (header ^ (Omd.of_string (String.concat "\n" article) |> Omd.to_html))
| _ -> None )
let () = @@ Dream.logger
@@ Dream.router
[ Dream.get "/assets/**" (Dream.static ~loader:asset_loader "")
; Dream.get "/blog/assets/**" (Dream.static ~loader:blog_asset_loader "")
; Dream.get "/" (fun _request ->
match page "index" with
| None -> Dream.empty `Not_Found
| Some content ->
(Template.render_unsafe ~lang:"en" ~title:"title" ~content) )
; Dream.get "/blog/:title" (fun request ->
match article (Dream.param "title" request) with
| None -> Dream.empty `Not_Found
| Some content ->
(Template.render_unsafe ~lang:"en" ~title:"title" ~content) )
; Dream.get "/:page" (fun request ->
match page (Dream.param "page" request) with
| None -> Dream.empty `Not_Found
......@@ -3,33 +3,33 @@ let render_unsafe ~lang ~title ~content =
<html lang="<%s lang %>">
<title><%s title %> | OCamlPro</title>
<link rel="icon" type="image/svg+xml" href="assets/img/logo_ocp_icon.svg">
<link href="assets/css/bootstrap.min.css" rel="stylesheet"/>
<link href="assets/css/style.css" rel="stylesheet">
<link rel="icon" type="image/svg+xml" href="/assets/img/logo_ocp_icon.svg">
<link href="/assets/css/bootstrap.min.css" rel="stylesheet"/>
<link href="/assets/css/style.css" rel="stylesheet">
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="index"><img src="assets/img/logo_ocamlpro.png" alt="OCamlPro" height="42" /></a>
<a class="navbar-brand" href="/"><img src="/assets/img/logo_ocamlpro.png" alt="OCamlPro" height="42" /></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav me-auto mb-2 mb-md-0">
<li class="nav-item">
<a class="nav-link" href="research-and-development">R&amp;D</a>
<a class="nav-link" href="/research-and-development">R&amp;D</a>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDarkDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="navbarDarkDropdownMenuLink">
<li><a class="dropdown-item" href="course_ocaml_development">OCaml Development</a></li>
<li><a class="dropdown-item" href="course_ocaml_expert">OCaml Expert</a></li>
<li><a class="dropdown-item" href="course_ocaml_code_opti">OCaml Code Optimization</a></li>
<li><a class="dropdown-item" href="course_rust_vocational_training">Rust Vocational Training</a></li>
<li><a class="dropdown-item" href="course_mastering_opam_ocaml_tools">Mastering OPAM &amp; OCaml Tools</a></li>
<li><a class="dropdown-item" href="/course_ocaml_development">OCaml Development</a></li>
<li><a class="dropdown-item" href="/course_ocaml_expert">OCaml Expert</a></li>
<li><a class="dropdown-item" href="/course_ocaml_code_opti">OCaml Code Optimization</a></li>
<li><a class="dropdown-item" href="/course_rust_vocational_training">Rust Vocational Training</a></li>
<li><a class="dropdown-item" href="/course_mastering_opam_ocaml_tools">Mastering OPAM &amp; OCaml Tools</a></li>
<li><a class="dropdown-item" href="">Custom Training</a></li>
<li class="nav-item dropdown">
......@@ -44,13 +44,13 @@ let render_unsafe ~lang ~title ~content =
<li><a class="dropdown-item" href="">OCamlPro on Github!</a></li>
<li class="nav-item">
<a class="nav-link" href="team">Team</a>
<a class="nav-link" href="/team">Team</a>
<li class="nav-item">
<a class="nav-link" href="jobs">Jobs</a>
<a class="nav-link" href="/jobs">Jobs</a>
<li class="nav-item">
<a class="nav-link" href="blog">Blog</a>
<a class="nav-link" href="/blog">Blog</a>
......@@ -73,10 +73,10 @@ let render_unsafe ~lang ~title ~content =
<a href="">Email</a> &middot;
<a href="tel:+33184800481">Phone</a> &middot;
<a href="">21 rue de Châtillon, 75014 Paris, France</a> &middot;
<a href="legal-notice">Legal Notice</a>
<a href="/legal-notice">Legal Notice</a>
<script src="assets/js/bootstrap.bundle.min.js"></script>
<script src="/assets/js/bootstrap.bundle.min.js"></script>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment