<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Erik Hofer</title>
    <description>The latest articles on DEV Community by Erik Hofer (@erikhofer).</description>
    <link>https://dev.to/erikhofer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F446292%2Fc7a9e992-561a-4f67-ba9c-c59d30a43f02.png</url>
      <title>DEV Community: Erik Hofer</title>
      <link>https://dev.to/erikhofer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/erikhofer"/>
    <language>en</language>
    <item>
      <title>Introducing scope42 - Improve your software architecture with precision! 🎯✨</title>
      <dc:creator>Erik Hofer</dc:creator>
      <pubDate>Tue, 25 Oct 2022 13:00:46 +0000</pubDate>
      <link>https://dev.to/scope42/introducing-scope42-improve-your-software-architecture-with-precision-dk6</link>
      <guid>https://dev.to/scope42/introducing-scope42-improve-your-software-architecture-with-precision-dk6</guid>
      <description>&lt;p&gt;As software architects, we often face legacy systems that have many architectural issues. Also in greenfield projects, the architecture is developed iteratively and needs to be improved on a regular basis.&lt;/p&gt;

&lt;center&gt;_Let's see how architecture improvement can be organized
 and assisted by tooling_ 🚀&lt;/center&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction to aim42&lt;/li&gt;
&lt;li&gt;
The birth of scope42 

&lt;ul&gt;
&lt;li&gt;Management of items&lt;/li&gt;
&lt;li&gt;Relationship graphs&lt;/li&gt;
&lt;li&gt;Docs-as-code &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;State of the project &amp;amp; roadmap &lt;/li&gt;

&lt;li&gt;Tech stack&lt;/li&gt;

&lt;li&gt;Your feedback is welcome ❤ &lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction to aim42
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;aim42&lt;/em&gt;, the Architecture Improvement Method, is an open, community-driven framework for working on software architectures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;aim42 supports software evolution, maintenance, migration and improvement - in a systematic and pragmatic way&lt;/p&gt;

&lt;p&gt;— &lt;cite&gt;&lt;a href="https://aim42.github.io/#_about_aim42" rel="noopener noreferrer"&gt;About aim42&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It defines a process consisting of three phases&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyze&lt;/li&gt;
&lt;li&gt;Evaluate&lt;/li&gt;
&lt;li&gt;Improve&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;accompanied by cross-cutting concerns. The method reference collects concepts and patterns that can be used to implement the process. It proposes three main types of entities of special interest:&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Issue&lt;/em&gt; (problem)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Risk&lt;/em&gt; (potential future problem)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Improvement&lt;/em&gt; (remedy)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instances of these, I'll call them &lt;em&gt;items&lt;/em&gt;, need to be collected, managed, and associated with each other. This is where the question of tooling comes into play.&lt;/p&gt;

&lt;p&gt;aim42 is licensed under Creative Commons Attributions Sharealike. For more information refer to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.aim42.org/" rel="noopener noreferrer"&gt;The official website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aim42.github.io/" rel="noopener noreferrer"&gt;The Method Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aim42/aim42" rel="noopener noreferrer"&gt;The GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The birth of scope42
&lt;/h2&gt;

&lt;p&gt;I came into touch with aim42 in the course of my master's thesis which was concerned with improving the software architecture of a legacy system. I approached this with my standard means of note-taking but realized at some point that a dedicated tool would be beneficial. This birthed the idea of &lt;em&gt;scope42&lt;/em&gt; and I started to work on it after finishing my thesis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftesn9ssam3ar5e9lxbgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftesn9ssam3ar5e9lxbgg.png" alt="scope42 logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;[github.com/scope42/scope42](https://github.com/scope42/scope42)&lt;/center&gt;

&lt;h3&gt;
  
  
  Management of items
&lt;/h3&gt;

&lt;p&gt;One of the first challenges that arise, as the number of items grows, is managing them. This requires structured metadata (e.g. title, status) as well as a way to filter and sort by it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwd9zmahkby4qkcywsu5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwd9zmahkby4qkcywsu5.png" alt="scope42 items table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the core functionality of scope42. It is similar to a ticket tracker. An existing generic one would require quite some configuration work to get started - scope42 comes with the aim42 domain model out-of-the-box. It even incorporates some content of the aim42 method reference so you can learn it along the way.&lt;/p&gt;

&lt;p&gt;Also, I try to provide a modern and slick UI that is not overloaded but focused on the particular domain. The aim is to make the act of writing documentation as fun as possible. At least in my personal experience, this is already quite successful.&lt;/p&gt;

&lt;p&gt;Another notable feature is the integrated full-text search and quick access to items via the &lt;code&gt;Strg + K&lt;/code&gt; / &lt;code&gt;⌘ + K&lt;/code&gt; shortcut. &lt;/p&gt;

&lt;h3&gt;
  
  
  Relationship graphs
&lt;/h3&gt;

&lt;p&gt;While you can link tickets in classical trackers like JIRA, visualization of these relationships is not a common feature.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9l6x1p82lk55vot3e4lw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9l6x1p82lk55vot3e4lw.png" alt="scope42 items graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The connection of items (e.g. issue is caused by another issue, improvement mitigates risk, etc.) are at the core of the aim42 domain model. It is an important concern and the overview can get lost quickly with a growing number of items.&lt;/p&gt;

&lt;p&gt;Because of this, scope42 features relationship graphs for visualization. On an item's details page, you can quickly see its direct connections. Also, when viewing a list of items (e.g. filtered by specific criteria), a graph displays the relationship between these items.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docs-as-code
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://app.scope42.org" rel="noopener noreferrer"&gt;scope42 web app&lt;/a&gt; runs in your browser - no need to download anything. However, a vital difference to most such applications is how the data is stored. There is no database.&lt;/p&gt;

&lt;p&gt;All items reside as files on your local machine. The app interacts with them via the &lt;a href="https://web.dev/file-system-access/" rel="noopener noreferrer"&gt;File System Access API&lt;/a&gt; (currently sadly does not work in Firefox, only in Chromium-based browsers). This ensures &lt;em&gt;privacy&lt;/em&gt; (data is never sent anywhere) and &lt;em&gt;data ownership&lt;/em&gt; - if scope42 disappears tomorrow, you will not lose your data.&lt;/p&gt;

&lt;p&gt;Another important aspect of data ownership is an open format. You should, at any point, be able to take your data and use it somewhere else. In fact, scope42 is built around the idea of processing the structured data managed with it and e.g. incorporating it into your existing documentation.&lt;/p&gt;

&lt;p&gt;A main driver for the choice of a concrete format is the concept of &lt;em&gt;docs-as-code&lt;/em&gt;. It is based on the idea that documentation is stored in a lightweight text-based format. It can then - similar to or even alongside code - be checked into version control, authored with your editor/IDE, and automatically processed and tested. You can read more about this topic at &lt;a href="https://docs-as-co.de/" rel="noopener noreferrer"&gt;docs-as-co.de&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Applying this approach to more structured data is a bit of a challenge. As a compromise between human-readability and machine-readability, a format based on YAML is used. Example (improvement):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upgrade Spring Boot version&lt;/span&gt;
&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2022-05-02T17:25:16.909Z&lt;/span&gt;
&lt;span class="na"&gt;modified&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2022-05-02T18:09:42.952Z&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;implemented&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;urgent&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;security&lt;/span&gt;
&lt;span class="na"&gt;ticket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/scope42/scope42/issues/91&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;The current version of Spring Boot upgrades dependencies to secure&lt;/span&gt;
  &lt;span class="s"&gt;versions of Log4j.&lt;/span&gt;
&lt;span class="na"&gt;resolves&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;issue-3&lt;/span&gt;
&lt;span class="na"&gt;modifies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;risk-1&lt;/span&gt;
&lt;span class="na"&gt;comments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Jane Doe&lt;/span&gt;
    &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2022-05-02T17:27:17.630Z&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Accepted and placed into the fast lane on the board.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In many places (for example item descriptions and comments), Markdown is supported for formatting. This includes advanced features like GFM extensions, code syntax highlighting, links to scope42 items, Mermaid diagrams, and more to come. For more information see &lt;a href="https://docs.scope42.org/markdown" rel="noopener noreferrer"&gt;docs.scope42.org/markdown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For collaboration, you can use your file-syncing technology of choice. It is recommended to check your scope42 data in a Git repository. As for your code, this enables a detailed change history and the ability to merge concurrent modifications.&lt;/p&gt;
&lt;h2&gt;
  
  
  State of the project &amp;amp; roadmap
&lt;/h2&gt;

&lt;p&gt;I already use scope42 for multiple real architecture projects at work. The experience from these - and iterative improvements based upon it - have led to the first stable release being recently published.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1582788370584940544-135" src="https://platform.twitter.com/embed/Tweet.html?id=1582788370584940544"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1582788370584940544-135');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1582788370584940544&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;It is still in a relatively early stage in terms of features that I have in mind. But the release of version 1.0.0 means that there will be no breaking changes to the data format in the foreseeable future. You can safely start to use it and will be provided with automatic migrations if necessary at some point.&lt;/p&gt;

&lt;p&gt;The project is completely open-source and free software. It is published under GPLv3 on GitHub.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/scope42" rel="noopener noreferrer"&gt;
        scope42
      &lt;/a&gt; / &lt;a href="https://github.com/scope42/scope42" rel="noopener noreferrer"&gt;
        scope42
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🎯 Improve your software architecture with precision!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/scope42/scope42/main/app/public/logo.svg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fscope42%2Fscope42%2Fmain%2Fapp%2Fpublic%2Flogo.svg" width="100%" alt="scope42 logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Improve your software architecture with precision!&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;a href="https://github.com/scope42/scope42/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/scope42/scope42/actions/workflows/build.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;
  &lt;a href="http://makeapullrequest.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d88d8d77fa79e828eea397f75a1ebd114d13488aeec4747477ffbd2274de95ed/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667" alt="PRs Welcome"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/scope42/scope42/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0cd8f39bfbe65dd5c1d51cbac408707f714276ce8347a9e7201e6f07db818d2b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c2d2d332e302d696e666f726d6174696f6e616c2e737667" alt="License: GPL v3"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/scope42/scope42/tree/main/architecture" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2156c73343b7638a1ec301a4dcf1ae18e7af9c9edd5b28c8a00575f1e78e8d4e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2546302539462539332539362532306172636869746563747572652d73636f706534322d626c7565" alt="Architecture Documentation"&gt;&lt;/a&gt;
  &lt;br&gt;
  &lt;a href="https://matrix.to/#/#scope42:matrix.org" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fd21acc439db0f7ef63afbb0cfb4085a696e11e797ad108b5a3e2a097eb9ac09/68747470733a2f2f696d672e736869656c64732e696f2f6d61747269782f73636f706534323a6d61747269782e6f7267" alt="Matrix"&gt;&lt;/a&gt;
  &lt;a href="https://twitter.com/scope42_org" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ae9b9aa9e31823fa258c7537ce1ea3413061bd54a33c3e23c162dc029ceaba4c/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f73636f706534325f6f72673f7374796c653d736f6369616c" alt="Twitter Follow"&gt;&lt;/a&gt;
  &lt;a href="https://floss.social/@scope42" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e4f3b7526c435fe76cc4a83081cfc4717df69ddf04b7515956a024c9060b7aca/68747470733a2f2f696d672e736869656c64732e696f2f6d6173746f646f6e2f666f6c6c6f772f3130383230323633363636343235333536313f646f6d61696e3d6874747073253341253246253246666c6f73732e736f6369616c267374796c653d736f6369616c" alt="Mastodon Follow"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;This tool helps you to keep track of issues, arising risks and possible improvements of your existing architecture. The terminology and concepts are based on &lt;a href="https://www.aim42.org/" rel="nofollow noopener noreferrer"&gt;aim42, the Architecture Improvement Method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;scope42 is a Progressive Web App that runs entirely inside your browser. Click the link below to access the app.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🔗 &lt;a href="https://app.scope42.org" rel="nofollow noopener noreferrer"&gt;app.scope42.org&lt;/a&gt;
&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;✨ Management of items with a fancy UI&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;
  Filtering • Sorting • Full text search • Learn about aim42
&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/scope42/scope42docs/screenshot-table.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fscope42%2Fscope42docs%2Fscreenshot-table.png" alt="Screenshot of item table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;💑 Graphs for visualizing the relationships between items&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;
  Quick overview • Drag &amp;amp; Drop • Navigate
&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/scope42/scope42docs/screenshot-graph.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fscope42%2Fscope42docs%2Fscreenshot-graph.png" alt="Screenshot of item graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📝 Docs-as-Code principle and full data ownership&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;
  Human-readable • Check into version control • Process programatically • No vendor lock-in
&lt;/p&gt;



&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;title&lt;/span&gt;: &lt;span class="pl-s"&gt;Upgrade Spring Boot version&lt;/span&gt;
&lt;span class="pl-ent"&gt;created&lt;/span&gt;: &lt;span class="pl-s"&gt;2022-05-02T17:25:16.909Z&lt;/span&gt;
&lt;span class="pl-ent"&gt;modified&lt;/span&gt;: &lt;span class="pl-s"&gt;2022-05-02T18:09:42.952Z&lt;/span&gt;
&lt;span class="pl-ent"&gt;status&lt;/span&gt;: &lt;span class="pl-s"&gt;implemented&lt;/span&gt;
&lt;span class="pl-ent"&gt;tags&lt;/span&gt;
  - &lt;span class="pl-s"&gt;backend&lt;/span&gt;
  - &lt;span class="pl-s"&gt;urgent&lt;/span&gt;
  - &lt;span class="pl-s"&gt;security&lt;/span&gt;
&lt;span class="pl-ent"&gt;ticket&lt;/span&gt;: &lt;span class="pl-s"&gt;https://github.com/scope42/scope42/issues/91&lt;/span&gt;
&lt;span class="pl-ent"&gt;description&lt;/span&gt;: &lt;span class="pl-s"&gt;The current version of Spring Boot upgrades&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/scope42/scope42" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;center&gt;**The app is available at [app.scope42.org](https://app.scope42.org).**&lt;/center&gt;



&lt;p&gt;There is a large roadmap of planned features I am eager to implement. These for example include UX improvements like an advanced Markdown editor, more Markdown features, and item management capabilities.&lt;/p&gt;

&lt;p&gt;In the long run, scope42 will be expanded from an architecture improvement tool to a general architecture documentation platform. In most projects, these two concerns will naturally play together. For example, if you use the &lt;a href="https://arc42.org/overview" rel="noopener noreferrer"&gt;arc42&lt;/a&gt; template for your documentation, the aim42 items would be integrated into chapter 11 "risks and technical debt".&lt;/p&gt;

&lt;p&gt;In fact, there is already an additional item type &lt;em&gt;decision&lt;/em&gt; (&lt;a href="https://adr.github.io/" rel="noopener noreferrer"&gt;ADR&lt;/a&gt;). This falls more into the realm of documentation (arc42 chapter 9). However, it quickly became clear that ADRs are also connected to aim42. Often, proposed improvements need to be assessed by architecture decisions, especially if there are multiple options to resolve an issue.&lt;/p&gt;

&lt;p&gt;In addition to this, the focus will be laid on exporting data and integration with other tooling. This includes a convenience library for programmatic processing, static site export, and a set of recipes, e.g. for integrating with things like &lt;a href="https://github.com/doctoolchain/doctoolchain" rel="noopener noreferrer"&gt;docToolchain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, a great advantage of this application over a static collection of Markdown/AsciiDoc files will be dynamic adaption for specific tasks. It should be possible to view your documentation from different &lt;a href="https://www.viewpoints-and-perspectives.info/home/perspectives/" rel="noopener noreferrer"&gt;architectural perspectives&lt;/a&gt;. This principle is for example implemented for diagrams by &lt;a href="https://www.structurizr.com/help/perspectives" rel="noopener noreferrer"&gt;Structurizr&lt;/a&gt;. It will be applied more universally by scope42. Also, it may be useful to tailor your documentation to different target audiences (e.g. developers, management) as described by arc42.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code is written in &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;UI is built with &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://ant.design/" rel="noopener noreferrer"&gt;Ant Design&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Relationship graphs are created using &lt;a href="https://js.cytoscape.org/" rel="noopener noreferrer"&gt;Cytoscape.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Code is organized in a monorepo powered by &lt;a href="https://turborepo.org/" rel="noopener noreferrer"&gt;Turborepo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The CI/CD pipeline is based on &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; and &lt;a href="https://github.com/changesets/changesets" rel="noopener noreferrer"&gt;Changesets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The production version of the app and deployment previews for pull requests are hosted on &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tech choices and other architecture decisions are documented using scope42 itself &lt;a href="https://github.com/scope42/scope42/tree/main/architecture" rel="noopener noreferrer"&gt;in the repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your feedback is welcome ❤
&lt;/h2&gt;

&lt;p&gt;If you think this app could be useful to you, don't hesitate to &lt;a href="https://app.scope42.org" rel="noopener noreferrer"&gt;try it out&lt;/a&gt; yourself!&lt;/p&gt;

&lt;p&gt;If you have any feedback, feel free to leave a comment here, open an issue/discussion on &lt;a href="https://github.com/scope42/scope42" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or get in touch with &lt;a href="https://twitter.com/scope42_org" rel="noopener noreferrer"&gt;@scope42_org&lt;/a&gt; on Twitter.&lt;/p&gt;

&lt;p&gt;My dream would be to build a community around this tool that shapes it so we can all profit from each other and make our daily work even more fun and effective 🚀&lt;/p&gt;

&lt;p&gt;Following for updates on any platform is greatly appreciated - as well as sharing this article with people you think may be interested ❤&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;In the actual &lt;a href="https://aim42.github.io/#Domain-Model" rel="noopener noreferrer"&gt;aim42 domain model&lt;/a&gt;, risk is not a primary entity type but an extension of issue. In scope42 I decided to make the distinction more sharp. Also, there is another entity type &lt;em&gt;cause&lt;/em&gt; in the method reference. I modeled this as a relationship between issues/risks instead. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://obsidian.md/" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt; is good at visualizing relationships and would adhere to docs-as-code. However, it is only free for personal use. Software architecture is often worked on in a commercial setting. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>architecture</category>
      <category>productivity</category>
      <category>news</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Publish to DokuWiki programmatically without any API</title>
      <dc:creator>Erik Hofer</dc:creator>
      <pubDate>Tue, 20 Sep 2022 17:16:57 +0000</pubDate>
      <link>https://dev.to/erikhofer/publish-to-dokuwiki-programmatically-without-any-api-4b6o</link>
      <guid>https://dev.to/erikhofer/publish-to-dokuwiki-programmatically-without-any-api-4b6o</guid>
      <description>&lt;p&gt;In a project I am working on we use &lt;a href="https://www.dokuwiki.org/dokuwiki" rel="noopener noreferrer"&gt;DokuWiki&lt;/a&gt; for our documentation. In addition to the manually written text, I wanted to integrate some automatically generated content that is updated every day.&lt;/p&gt;

&lt;p&gt;There is a &lt;a href="https://www.dokuwiki.org/plugin:api" rel="noopener noreferrer"&gt;Plugin that provides a REST API&lt;/a&gt; but natively, there is only &lt;a href="https://www.dokuwiki.org/devel:xmlrpc" rel="noopener noreferrer"&gt;XML-RPC&lt;/a&gt;. Not that fancy, but for simply pushing simple page content, this would be sufficient. So I tried a &lt;code&gt;fetch&lt;/code&gt; version of the &lt;a href="https://www.dokuwiki.org/devel:xmlrpc:clients#sample_jquery_client" rel="noopener noreferrer"&gt;sample jQuery client&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ Note that the following examples use the &lt;a href="https://dev.to/andrewbaisden/the-nodejs-18-fetch-api-72m"&gt;native &lt;code&gt;fetch&lt;/code&gt; API&lt;/a&gt; that required Node 18+.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WIKI_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/dokuwiki&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;?xml version="1.0"?&amp;gt;
  &amp;lt;methodCall&amp;gt;
    &amp;lt;methodName&amp;gt;wiki.getRPCVersionSupported&amp;lt;/methodName&amp;gt;
    &amp;lt;params&amp;gt;&amp;lt;/params&amp;gt;
  &amp;lt;/methodCall&amp;gt;`&lt;/span&gt;

&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/lib/exe/xmlrpc.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It printed the follwing response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;methodResponse&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;fault&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;value&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;struct&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;member&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;faultCode&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;value&amp;gt;&amp;lt;int&amp;gt;&lt;/span&gt;-32605&lt;span class="nt"&gt;&amp;lt;/int&amp;gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/member&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;member&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;faultString&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;value&amp;gt;&amp;lt;string&amp;gt;&lt;/span&gt;XML-RPC server not enabled.&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/member&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/struct&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/fault&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/methodResponse&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uh, bummer 😐&lt;br&gt;
Now, the DokuWiki instance is shared among multiple teams. Installing a plugin or enabling the XML-RPC API would involve at least some bureaucracy.&lt;/p&gt;

&lt;p&gt;Because of that, I tried to come up with a solution &lt;strong&gt;without any API&lt;/strong&gt;. I mean, if I can do it in my browser, there must be a way for a script to do it, right?&lt;/p&gt;

&lt;p&gt;So the first thing I did was looking at what DokuWiki does internally when I edit a page in the browser. In principle, it is a simple HTML form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frua1ywq3bi4g7jmsways.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frua1ywq3bi4g7jmsways.png" alt="DokuWiki page edit form data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The form data shows that crafting a corresponding HTTP request is not that simple. We probably need to read some values from the form or reverse-engineer how to produce them. Especialy &lt;code&gt;sectok&lt;/code&gt; and &lt;code&gt;changecheck&lt;/code&gt; look suspicious.&lt;/p&gt;

&lt;p&gt;To save time on that, my first impulse was to use a UI automation tool like &lt;a href="https://www.selenium.dev/" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; but luckily, I came up with a way simpler solution.&lt;/p&gt;

&lt;p&gt;It is based on &lt;a href="https://cheerio.js.org/" rel="noopener noreferrer"&gt;cheerio&lt;/a&gt;, an "implementation of core jQuery designed specifically for the server". With this, crafting the request becomes as easy as submitting a form with jQuery (almost).&lt;/p&gt;

&lt;p&gt;But first, we need to sign in. For this, we have to make &lt;code&gt;fetch&lt;/code&gt; support cookies (side note, even with XML-RPC, &lt;a href="https://www.dokuwiki.org/devel:xmlrpc#dokuwikilogin" rel="noopener noreferrer"&gt;this would have been the case&lt;/a&gt; 🙄). I did it by utilizing the package &lt;a href="https://www.npmjs.com/package/fetch-cookie" rel="noopener noreferrer"&gt;fetch-cookie&lt;/a&gt;. Then, I just looked how the login form of DokuWiki works and came up with the following request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;makeFetchCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch-cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeFetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WIKI_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/dokuwiki&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/doku.php?do=login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;u&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PASSWORD&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, we can request the editing form for the page we want to update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PAGE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path/to/page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/doku.php/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PAGE_PATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?do=edit`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We load the HTML into cheerio and look for the text area with the ID &lt;code&gt;wiki__text&lt;/code&gt;. Then we can update the content of the text area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cheerio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textarea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;textarea#wiki__text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is the new, //generated//, page content.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that if you want to replace parts of the exsiting text, you can get the current content by calling &lt;code&gt;textarea.text()&lt;/code&gt; without the argument.&lt;/p&gt;

&lt;p&gt;We can now get the content body for our POST request in the following way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form#dw__editform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;do[save]=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The addition of &lt;code&gt;do[save]=&lt;/code&gt; is needed because that would be added when clicking the submit button. Without this, no data will actually be saved.&lt;/p&gt;

&lt;p&gt;Finally, we can craft and send the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/doku.php/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PAGE_PATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?do=edit`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We successfully published to DokuWiki programmatically without any API 🎉&lt;/p&gt;

&lt;p&gt;Full example code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cheerio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;makeFetchCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch-cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeFetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WIKI_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/dokuwiki&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PAGE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path/to/page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/doku.php?do=login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;u&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PASSWORD&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/doku.php/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PAGE_PATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?do=edit`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cheerio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textarea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;textarea#wiki__text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;textarea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is the new, //generated//, page content.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form#dw__editform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;do[save]=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;WIKI_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/doku.php/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PAGE_PATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?do=edit`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>dokuwiki</category>
      <category>tutorial</category>
      <category>node</category>
    </item>
    <item>
      <title>Build and Publish a Multi-Platform Electron App on GitHub</title>
      <dc:creator>Erik Hofer</dc:creator>
      <pubDate>Thu, 31 Dec 2020 15:50:18 +0000</pubDate>
      <link>https://dev.to/erikhofer/build-and-publish-a-multi-platform-electron-app-on-github-3lnd</link>
      <guid>https://dev.to/erikhofer/build-and-publish-a-multi-platform-electron-app-on-github-3lnd</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Create Electron Application&lt;/li&gt;
&lt;li&gt;Build on Linux, Windows and macOS&lt;/li&gt;
&lt;li&gt;Publish Release to GitHub&lt;/li&gt;
&lt;li&gt;Draft Releases&lt;/li&gt;
&lt;li&gt;Outlook&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;I recently dived into &lt;a href="https://www.electronjs.org/" rel="noopener noreferrer"&gt;Electron&lt;/a&gt; for a hobby project. I like the approach of using web technologies to create the UI and shipping a native application to all platforms.&lt;/p&gt;

&lt;p&gt;For a Proof of Concept, I wanted to actually set up the process of building and distributing such an application on GitHub. It was a lot of trial and error to finally get there. Because of this, I documented my learnings in this tutorial. The final setup I came up with is actually surprisingly simple.&lt;/p&gt;

&lt;p&gt;You can find the complete example here:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/erikhofer" rel="noopener noreferrer"&gt;
        erikhofer
      &lt;/a&gt; / &lt;a href="https://github.com/erikhofer/electron-publish-example" rel="noopener noreferrer"&gt;
        electron-publish-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Example repository for building and publishing a multi-platform Electron app
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  Create Electron Application &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;We are going to use &lt;a href="https://www.electronforge.io/" rel="noopener noreferrer"&gt;Electron Forge&lt;/a&gt; for creating the example application. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npx create-electron-app electron-publish-example
cd electron-publish-example
npm start


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can now see our example application in a native window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcfdnolemnidbsm6my3h3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcfdnolemnidbsm6my3h3.png" alt="Example App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The distributable packages can be built with the following command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm run make


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will only build package formats that are supported by your operating system. For more information see &lt;a href="https://www.electronforge.io/config/makers" rel="noopener noreferrer"&gt;Electron Forge Makers&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Build on Linux, Windows and macOS &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Ok, so how do we build the application for other platforms? Luckily, &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; provides a free and easy way to do this. We start by creating an empty repository and pushing our example code. I assume that you know the basics of Git. Everything we do from now on needs to be pushed to GitHub.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git init
git add .
git commit -m "Create example app"
git branch -M main
git remote add origin https://github.com/erikhofer/electron-publish-example.git
git push -u origin main


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We then create a new file &lt;code&gt;.github/workflows/build.yml&lt;/code&gt; with the follwoing content.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_on_linux&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run make&lt;/span&gt;

  &lt;span class="na"&gt;build_on_mac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run make&lt;/span&gt;

  &lt;span class="na"&gt;build_on_win&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run make&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It basically defines the same job three times for different operating systems. The workflow is executed for all branches and for pull requests to verify that the application can still be built after making changes.&lt;/p&gt;

&lt;p&gt;After pushing the file, we go to the "Actions" tab of the repository (&lt;a href="https://github.com/erikhofer/electron-publish-example/actions" rel="noopener noreferrer"&gt;example&lt;/a&gt;). We can see our newly created workflow running. Click on it to see the individual tasks and wait for them to finish.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fovls2lpibcdl46jm3d77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fovls2lpibcdl46jm3d77.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! 🚀 We are now building a native application on Windows, Linux and macOS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Publish Release to GitHub &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Fine, now how do we get access to the distributable files? We could set up &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/guides/storing-workflow-data-as-artifacts" rel="noopener noreferrer"&gt;artifact uploading&lt;/a&gt; for this. While this is useful for developers, it is not sufficient for providing the application to users. For that, we want to utilize &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/managing-releases-in-a-repository" rel="noopener noreferrer"&gt;GitHub releases&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmeu46l91dzprj6xqwjel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmeu46l91dzprj6xqwjel.png" alt="Shipping"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A release is based on a &lt;a href="https://git-scm.com/book/en/v2/Git-Basics-Tagging" rel="noopener noreferrer"&gt;Git tag&lt;/a&gt;. It has a descriptive text (e.g. changelog) and can have files attached to it. That's exactly what we need.&lt;/p&gt;

&lt;p&gt;Electron Forge provides a &lt;a href="https://www.electronforge.io/config/publishers/github" rel="noopener noreferrer"&gt;GitHub publisher&lt;/a&gt; that does all the work for us. We need to install it in our example project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm install -D @electron-forge/publisher-github


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we add the following configuration to the &lt;code&gt;package.json&lt;/code&gt; file (make sure to adapt this to your repository).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forge"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"publishers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@electron-forge/publisher-github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"erikhofer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electron-publish-example"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally, we create a second workflow &lt;code&gt;.github/workflows/release.yml&lt;/code&gt; with the following content.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;created&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;publish_on_linux&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;       
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run publish&lt;/span&gt;

  &lt;span class="na"&gt;publish_on_mac&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run publish&lt;/span&gt;

  &lt;span class="na"&gt;publish_on_win&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;       
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run publish&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It is basically the same as &lt;code&gt;build.yaml&lt;/code&gt; but uses the &lt;code&gt;publish&lt;/code&gt; script (&lt;code&gt;make&lt;/code&gt; is not needed, this would build the app twice). It also accesses the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; and is only executed for created releases.&lt;/p&gt;

&lt;p&gt;After pushing all changes, we can go to the "Releases" section in the "Code" tab of the repository and click "Create a new release". As "Tag version" we chose "v1.0.0" and click "Publish release". In the "Actions" tab we can now see that our newly created workflow is executed.&lt;/p&gt;

&lt;p&gt;After it has finished, we go back to our release (&lt;a href="https://github.com/erikhofer/electron-publish-example/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;example&lt;/a&gt;). It should now have the packaged application for all platforms attached.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc9ch2ztfju7b9atb04f9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc9ch2ztfju7b9atb04f9.png" alt="Release"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Draft Releases &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;So far we pushed a tag and then manually created a release via the GitHub website. With this approach, the release is published immediately and the files are attached later—assuming the action is executed successfully. If something goes wrong, watchers have already been notified about the release via email.&lt;/p&gt;

&lt;p&gt;Ideally, we want to draft a release, attach the application files and then publish, if everything looks good. There is, however, a caveat.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;release&lt;/code&gt; event is not triggered for draft releases. (&lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#release" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means that if we create a draft release and then publish it, the &lt;code&gt;created&lt;/code&gt; activity is never detected. If we use &lt;code&gt;published&lt;/code&gt; instead, we still have the same behavior as before.&lt;/p&gt;

&lt;p&gt;We can solve this by letting Electron Forge create the release. For that, we first change the workflow so that it is executed for all version tags.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is sufficient for automatically creating releases. Additionally, we can now configure the publisher to create a draft instead.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"publishers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@electron-forge/publisher-github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"erikhofer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello-electron"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"draft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To create tags, we can utilize the &lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-version" rel="noopener noreferrer"&gt;&lt;code&gt;npm version&lt;/code&gt;&lt;/a&gt; command. It automatically updates the &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt;. Let's create a new version &lt;code&gt;1.1.0&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm version minor
git push --follow-tags


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;⚠ Make sure to push the created tag to GitHub. By default, Git does not push tags.&lt;/p&gt;

&lt;p&gt;After the workflow has finished, we go to the releases page again. We can now see the automatically created draft release.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg6css3hjjhq6r5ohseg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg6css3hjjhq6r5ohseg6.png" alt="Draft Release"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything looks fine, click on "Edit", enter a description and click "Publish release". We now have a proper setup for distributing our multi-platform Electron application. ✨&lt;/p&gt;

&lt;h1&gt;
  
  
  Outlook &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;If we actually want to pubslish a software that is used by the public, we need to address &lt;a href="https://www.electronjs.org/docs/tutorial/code-signing" rel="noopener noreferrer"&gt;code signing&lt;/a&gt; as the next step. It is a security mechanism and at least on macOS, there is no practical way around it. It is also needed for auto-updating the application on other platforms.&lt;/p&gt;

&lt;p&gt;Electron Forge and other tools have convenient built-in support for the code signing process. But be aware that certificates need to be purchased with an anual fee.&lt;/p&gt;

&lt;p&gt;Speaking of auto-updating, this is an interesting topic to look into next. Now that we have a setup to host our binaries via GitHub releases, we can also use this for update distribution. There is even a completely free service for open source applications. For more information, take a look into the &lt;a href="https://www.electronforge.io/advanced/auto-update" rel="noopener noreferrer"&gt;Electron Forge docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>electron</category>
      <category>github</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>We created an Open Source Programming and Auto Evaluation Platform for CS Education</title>
      <dc:creator>Erik Hofer</dc:creator>
      <pubDate>Fri, 28 Aug 2020 12:18:07 +0000</pubDate>
      <link>https://dev.to/erikhofer/we-created-an-open-source-programming-and-auto-evaluation-platform-for-cs-education-nbj</link>
      <guid>https://dev.to/erikhofer/we-created-an-open-source-programming-and-auto-evaluation-platform-for-cs-education-nbj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z6_ewt00--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zon0wzr25j6pnml7w0k0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z6_ewt00--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zon0wzr25j6pnml7w0k0.png" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At our university there are still paper-based coding assignments in computer science classes. To make a step into the 21st century, we started developing Programming and Auto Evaluation Platform almost two years ago. We are now happy to anounce it to the public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free and open source under AGPL&lt;/li&gt;
&lt;li&gt;Programming language agnostic&lt;/li&gt;
&lt;li&gt;Flexible auto-evaluation system based on Docker&lt;/li&gt;
&lt;li&gt;In-browser IDE for zero-setup coding exercises&lt;/li&gt;
&lt;li&gt;Integration with Learning Management Systems via LTI standard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We still have a large roadmap ahead of us but we are already running in production from the MVP on. We continuously field-test with actual students and would love to get other educational institutions on board. If you are interested in trying in trying it out, please get in touch with us!&lt;/p&gt;

&lt;p&gt;The application is developed with Kotlin (Spring) in the backend and TypeScript (React) in the frontend. We are looking for contributors to make this a real community-driven project!&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="http://codefreak.org"&gt;codefreak.org&lt;/a&gt; or &lt;a href="https://github.com/codefreak/codefreak"&gt;github.com/codefreak/codefreak&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/codefreak"&gt;
        codefreak
      &lt;/a&gt; / &lt;a href="https://github.com/codefreak/codefreak"&gt;
        codefreak
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Online Programming Platform and Evaluation/Auto-Feedback System for Coding Assignments
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
    &lt;a rel="noopener noreferrer" href="https://raw.githubusercontent.com/codefreak/codefreak/master/client/public/logo192.png"&gt;&lt;img alt="Code FREAK Logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--IVdYv0Kr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/codefreak/codefreak/master/client/public/logo192.png"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;h1&gt;
Code FREAK&lt;/h1&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/codefreak/codefreak/workflows/CI/badge.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GX3H49Uf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/codefreak/codefreak/workflows/CI/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://hub.docker.com/r/cfreak/codefreak" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c548607f127c130052cfd37280419cb373f124ec81a1e28d64e9273c8840d552/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f762f63667265616b2f636f6465667265616b3f736f72743d73656d766572" alt="Docker Image Version"&gt;&lt;/a&gt;
&lt;a href="https://www.gnu.org/licenses/agpl-3.0" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/73e1990ad43e58f89f8165209bfce207dc0932d5c19ef97c8c7d5b3e003fbb99/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4147504c25323076332d696e666f726d6174696f6e616c2e737667" alt="License: AGPL v3"&gt;&lt;/a&gt;
&lt;a href="https://discord.gg/HYDQEDt" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ca51f32592964b69cadacd93eb213d534bdfa94c6f0e9167ae03c0f33346f278/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f3734383835363939373130353130373032353f636f6c6f723d373238396461266c6162656c3d646973636f7264266c6f676f3d646973636f7264266c6f676f436f6c6f723d666666666666" alt="Join Discord Server"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Code FREAK (Code Feedback, Review &amp;amp; Evaluation Kit) is an online programming platform and evaluation/autograding system for coding assignments. It supports every major programming language (language agnostic) and has a modular evaluation system based on Docker.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://raw.githubusercontent.com/codefreak/codefreak/master/./screenshot.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yNcNOjJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/codefreak/codefreak/master/./screenshot.png" alt="Code FREAK Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Main Features&lt;/h2&gt;
&lt;ul class="contains-task-list"&gt;
&lt;li class="task-list-item"&gt;
 Support for every major programming language (language agnostic)&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Pluggable evaluation system based on Docker (Dynamic Testing, Linting, …)&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 In-browser IDE based on VSCode/&lt;a href="https://github.com/cdr/code-server"&gt;Coder&lt;/a&gt;
&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Modern UI based on React and Ant Design&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Integrates with learn management systems (LMS) via LTI 1.3 standard&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 LDAP authentication&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 100% free and open source&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Installation&lt;/h2&gt;
&lt;p&gt;We currently only support installation via Docker. The image name is &lt;a href="https://hub.docker.com/r/cfreak/codefreak" rel="nofollow"&gt;&lt;code&gt;cfreak/codefreak&lt;/code&gt;&lt;/a&gt;. Check out its &lt;a href="https://hub.docker.com/r/cfreak/codefreak?tab=tags" rel="nofollow"&gt;tags&lt;/a&gt; for the latest version.&lt;/p&gt;
&lt;h3&gt;
Try with Docker 🐋
&lt;/h3&gt;
&lt;p&gt;You can try out Code FREAK locally. The only requirement is a working installation of Docker on your computer.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell js-code-highlight"&gt;
&lt;pre&gt;docker run -it --rm \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -p 8080:8080 \
    cfreak/codefreak&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The UI is accessible…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/codefreak/codefreak"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>contributorswanted</category>
      <category>showdev</category>
      <category>kotlin</category>
      <category>react</category>
    </item>
  </channel>
</rss>
