SilasBerger / teaching-website

My teaching website 👨‍🏫
https://teach.silasberger.ch
1 stars 0 forks source link
computer-science computer-science-education docusaurus education highschool informatik informatikunterricht school teacher teaching

Website

My teaching website, built with Docusaurus. Visit at https://teach.silasberger.ch/.

Install and build

For more examples, check out the CI workflows in .github.

Where do I start?

A good place to start is to run SITE=drafts yarn start and then visit http://localhost:3000/default/Components/.

General configuration

Environment variables

Config files

Examples: Common configuration workflows

Configuring a new site called mySite

Configuring a new script someScript for a site mySite

Content configuration

Pages

The pages root is defined in the SiteProperties#pagesRoot property in config/siteProperties/<site>.site-properties.ts for a given site. The contents of the pages root directory are served /.

Warning: Make sure that the URL paths for pages and scrips do not collide! Examples:

Scripts

A script is a unique collection of elements from the material library. As such, it is a function of the material library and a set of configurations, executed and applied by the sync mechanism. From a technical point of view, the sync mechanism is a parameterized file copy process, where the sources and destinations are defined through configuration.

The root directory for the material library is defined by builder-config.ts#MATERIAL_ROOT. The root directory for the scripts output directory is defined by builder-config.ts#SCRIPTS_ROOT. The latter contains all script roots for a given site after building for that site (SITE=mySite yarn run build).

There are two ways to specify which material library elements should be copied to a particular script: the scripts config and _markers. These two approaches can be combined can be combined at will, whereby multiple library elements may point to the same destination in the script. In such cases, the following precedence rules define which of these library elements (in this case, called source candidates) will be copied the contested destination:

  1. The candidate that was explicitly (i.e. not recursively) mapped by a mapping entry in the script config.
  2. Out of the candidates which explicitly (i.e. not recursively) matched a marker, the one with the highest specificity (=lower specificity number).
  3. The candidate that was implicitly mapped my a mapping entry in the script config (i.e. the child of an explicitly mapped parent directory).
  4. Out of the candidates which implicitly matched a marker (i.e. children of a parent directory that matched a marker), the one with the highest specificity (=lower specificity number).

The scripts configs file

An excerpt of the scripts configs file <mySite>.scriptsConfigs.yaml for a site mySite might look as follows:

{
  "english": { # this object defines a script named "english"; its URL path will be /english
    "markers": { # markers definition (see below)
      "english": 0, # marker "english" has specificity 0 (the lower the number, the higher the precedence)
      "languages": 1 # marker "languages" has specificity 1
    },
    "mappings": [ # mappings definition
      # recursively copy the /General-Information directory from the material library to /01-General-Information in
      # the script 
      {"section": "/01-General-Information", "material": "/General-Information"},
      {"section": "/02-Units/01-Unit-1", "material": "/English/Units/Unit-1"},
      {"section": "/02-Units/02-Unit-2", "material": "/English/Units/Unit-2"}
    ]
  },
  "programming": {
    "markers": {
      "programming": 0
    },
    "mappings": [
      {"section": "/01-General-Information", "material": "/General-Information"},
      {
        # recursively copy the /Computer-Science/Programming/01-Introduction directory from the material library
        # to /02-Introduction-to-Programming in the script, but...
        "section": "/02-Introduction-to-Programming",
        "material": "/Computer-Science/Programming/01-Introduction",
        # ...from /Computer-Science/Programming/01-Introduction, ignore the file 04-Loops/03-do-while.mdx and the
        # entire "06-Exceptions" directory
        "ignore": ["04-Loops/03-do-while.mdx", "06-Exceptions"]
      }
    ]
  }
}

Markers

If a directory or filename contains a segment of the form .[], it is considered marked. Any comma-separated values within the two brackets are considered markers. For example, the file hello.[foo,bar].md has two markers: foo and bar.

During the sync process for a given script, the following rules apply to all marked files (and directories):

Additional useful information about the behavior of markers:

The final result

Assuming a material tree that looks as follows:

├── General-Information
│   ├── Required-Materials.[english]
│   │   ├── 01-Stationary.md
│   │   └── 02-Books.md
│   ├── index.md
│   ├── 01-Organizational-Matters.md
│   ├── 02-Class-Rules.md
│   ├── 02-Class-Rules.[languages].md
│   ├── 03-Semester-Agenda.[programming].md
│   └── 03-Semester-Agenda.[english].md
├── Digital-Tools
│   ├── word-processing.md
│   └── programming-environment.[programming].md 
├── English
│   └── Units
│       ├── Unit-1
│       │   └── ...
│       └── Unit-2
│           └── ...
└── Computer-Science
    ├── Programming
    │   ├── 01-Introduction
    │   │   ├── 01-Hello-World
    │   │   │   └── ...
    │   │   ├── 02-Variables
    │   │   │   └── ...
    │   │   ├── 03-Conditionals
    │   │   │   └── ...
    │   │   ├── 04-Loops
    │   │   │   ├── index.md
    │   │   │   ├── 01-for.mdx
    │   │   │   ├── 02-while.mdx
    │   │   │   └── 03-do-while.mdx
    │   │   ├── 05-Functions
    │   │   │   └── ...
    │   │   └── 06-Exceptions
    │   │       └── ...
    │   └── 02-Classes-and-Objects
    ├── Algorithms
    │   └── ...
    └── Networks
        └── ...

The final english script at /path/to/scripts/english will look as follows:

├── 01-General-Information  # explicitly mapped, include all non-marked contents
│   ├── Required-Materials  # include directory because it was marked with [languages]
│   │   ├── 01-Stationary.md
│   │   └── 02-Books.md
│   ├── index.md
│   ├── 01-Organizational-Matters.md
│   ├── 02-Class-Rules.md  # source file: 02-Class-Rules.[languages].md
│   └── 03-Semester-Agenda.md  # source file: 03-Semester-Agenda.[english].md
└── 02-Units  # explicitly mapped, include all non-marked contents
    ├── 01-Unit-1 
    │   └── ...
    └── 02-Unit-2
        └── ..

The final programming script at /path/to/scripts/programming will look as follows:

├── 01-General-Information
│   ├── index.md
│   ├── 01-Organizational-Matters.md
│   ├── 02-Class-Rules.md  # source file: 02-Class-Rules.md (unmarked)
│   └── 03-Semester-Agenda  # source file: 03-Semester-Agenda.[programming].md
├── Digital-Tools  # parent included because child was marked with [programming]
│   └── programming-environment.md  # source file: programming-environment.[programming].md
└── 02-Introduction-to-Programming  # explicitly mapped, include all non-marked contents 
    ├── 01-Hello-World
    │   └── ...
    ├── 02-Variables
    │   └── ...
    ├── 03-Conditionals
    │   └── ...
    ├── 04-Loops
    │   ├── index.md
    │   ├── 01-for.mdx
    │   ├── 02-while.mdx
    │   └── # missing: 03-do-while.mdx (ignored)
    ├── 05-Functions
    │   └── ...
    └──  # missing: 06-Exceptions (ignored)

Concepts: Material, sites, pages, scripts, and the sync mechanism

Material

The material library is a central collection of sections and articles in the form of Markdown files. For every site, these sections and articles are selectively assembled into individual scripts as needed. The root directory for the material library is defined by builder-config.ts#MATERIAL_ROOT.

Sites

A site represents...

A site is defined by a set of configuration files and entries as described in the General configuration section.

Pages

The Docusaurus pages plugin is used to create on-off standalone pages. These are individual Markdown files that are not part of any particular script. Pages can for instance be used to create landing pages or entrypoints for navigating to scripts.

Each site defines a pages root directory in its config/siteProperties/<site>.site-properties.ts configuration file. The contents of that directory are served at /. Looking at the following folder structure:

content
└── pages
    └── mysite
        ├── index.md  # page
        └── greetings
            └── hello-world.md  # page

Assuming that we are building the site mysite, and that the pagesRoot for mysite is set to content/pages/mysite. We would then see the resulting HTML files for our markdown file served as follows:

Scripts

A script is a unique collection of elements from the material library. It is defined by an entry in the site's *.scriptsConfigs.yaml file. The contents of the script are arranged via mapping entries in that scripts config entry, as well as through matching markers between the scripts config and filenames in the material library.

When building the respective site, each script definition yields a docs root for the Docusaurus Docs plugin. On that site, that docs root is served at /<scriptId>.

The sync mechanism

The sync mechanism is used to assemble contents from the material library into each individual script, as defined by that scripts configuration in the site's *.scriptsConfigs.yaml file and any applicable markers in the material filenames. docusaurus.config.ts marks the entrypoint into the build and sync process.

Deployment

Legacy sites

Some legacy sites may still be available in the codebase (i.e. have a .site-properties.ts-file, etc.) but are no longer included in the deployment build matrix and are hence no longer being deployed on push. Currently, this is the case for the lerbermatt site.

Environment secrets (GitHub Actions)

The build_and_deploy_sites task in check_build_deploy.yml specifies the following environment:

environment: deploy_${{ matrix.site }}

From that, the runner automatically created an environment named deploy_${{ matrix.site }} on the first run with this config. Note that legacy environments (i.e. environments for sites which are no longer in the build matrix) will not be automatically removed. Environments can be modified here.

The secrets within these environments are available in the secrets context to use within the workflow spec. They must be explicitly included and specified as environment variables for the respective jobs, e.g.

steps:
    run: SITE=${{ matrix.site }} yarn run build
    env:
      CLIENT_ID: ${{ secrets.CLIENT_ID }}
      TENANT_ID: ${{ secrets.TENANT_ID }}
      APP_URL: ${{ secrets.APP_URL }}
      BACKEND_URL: ${{ secrets.BACKEND_URL }}
      API_URI: ${{ secrets.API_URI }}

If a field FOO is added as a variable, rather than a secret, it is instead available in the vars context: ${{ vars.FOO }}}.

If an environment for a particular site does not provide a given secret or variable, that value (and hence, the environment variable) will be undefined. If, at some point, we need a different login strategy for another site (e.g. username / password for teach), we would add additional environment variables / secrets to the workflow config, define the required secrets in the corresponding environment (in this example, the ones for username / pw in teach), and make sure that the build process can handle one "set" being undefined (the MSAL one for teach and the username / pw one for gbsl).