Repository: enso-org/enso
Pull request #13131: Simplify and promote usage of runEngineDistribution
Author: login: JaroslavTulach
Labels: name: CI: Ready to merge
description: This PR is eligible for automatic merge
name: CI: No changelog needed
description: Do not require a changelog entry for this PR.
Created: 2025-05-21T06:54:17Z | Merged: 2025-05-21T14:41:20Z

### Pull Request Description

- restores `runEngineDistribution` behavior (lost in refactorings) to run with `-ea`
- adds support for `runEngineDistribution --env NAME=value`
- updates documentation ([read the current version here](https://github.com/enso-org/enso/blob/b653828adf50f9a39cf40578fd67a2a0a2907bf8/docs/CONTRIBUTING.md)) to
   - remove  clearly outdated debris
   - promote usage of `runEngineDistribution`
   - comment out alternative ways
   - fix links from ToC
- fixes `NullPointerException` when using `runEngineDistribution --run file.enso`

### Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

- [x] The documentation has been updated, if necessary.
- [x] All code follows the
      [Scala](https://github.com/enso-org/enso/blob/develop/docs/style-guide/scala.md),



Diff:
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 7c57a2b93141..6fb5f182fbbc 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -23,12 +23,14 @@ sections of this document are linked below:
   - [Design Documentation](#design-documentation)
   - [System Requirements](#system-requirements)
   - [Getting the Sources](#getting-the-sources)
-  - [Getting Set Up \(Rust\)](#getting-set-up-rust)
-  - [Getting Set Up \(JVM\)](#getting-set-up-jvm)
-  - [Getting Set Up \(Documentation\)](#getting-set-up-documentation)
-  - [Building Enso](#building-enso)
-  - [Testing Enso](#testing-enso)
-  - [Running Enso](#running-enso)
+  - Configuration
+    - [Set Up Rust](#getting-set-up-rust)
+    - [Set Up Documentation](#getting-set-up-documentation)
+  - Enso Engine CLI
+    - [Building Enso Engine](#building-enso-engine)
+    - [Running Enso](#running-enso-from-cli)
+    - [Testing Enso](#testing-enso-libraries)
+  - [Running Enso IDE](#running-ide)
 - [Pull Requests](#pull-requests)
 - [Documentation](#documentation)
 - [Issue Triage](#issue-triage)
@@ -238,6 +240,12 @@ git clone https://github.com/enso-org/enso.git
 git clone git@github.com:enso-org/enso.git
 ```
 
+The recommended IDE for working with the sources is
+[VSCode with Enso extension](../tools/enso4igv/README.md). Alternativelly, use
+the IntelliJ with the official
+[Scala plugin](https://plugins.jetbrains.com/plugin/1347-scala) and import the
+`sbt` project.
+
 ### Getting Set Up (Rust)
 
 The Rust code in this repository requires a specific nightly rust toolchain, as
@@ -277,19 +285,26 @@ npx prettier --write <dir>
 
 ### Building Enso Engine
 
-There are multiple projects in this repository, but all of the engine parts can
-be built, run and tested using `sbt`. As long as your configuration is correct,
-with the correct versions of SBT, Rust and GraalVM, the same steps can be
-followed on all of our supported platforms (Linux, MacOS and Windows).
+There are multiple projects in the main Enso repository. All of the engine parts
+can be built, run and tested using
+[Scala Build Tool](https://www.scala-sbt.org/). As long as your configuration is
+correct, with the correct versions of `sbt`, Rust and GraalVM, the same steps
+can be followed on all of our supported platforms (Linux, MacOS and Windows).
+
+```sbt
+$ sbt
+sbt:enso> buildEngineDistribution
+```
 
-SBT will handle downloading and building library dependencies as needed, meaning
-that you don't need to handle any of this manually.
+The language interpreter CLI distribution is generated into `built-distribution`
+directory with `bin/enso` launcher script/executable located inside. `sbt`
+handles downloading and building library dependencies as needed, meaning that
+you don't need to handle any of this manually.
 
-**Please note** that at the current time, the Windows build of GraalVM is in an
-experimental state. This means that while it may function, we are not intending
-to provide work-arounds for building on that platform while it is still in an
-unstable state.
+One can use the generated binaries directly, but the suggested workflow is to
+continue using [sbt for execution](#running-enso-from-cli) as well.
 
+<!--
 #### Building Enso Components
 
 In order to build a specific component (e.g. `runtime`), please follow the
@@ -306,14 +321,6 @@ shell will execute the appropriate thing. Furthermore we have `testOnly` and
 `benchOnly` that accept a glob pattern that delineates some subset of the tests
 or benchmarks to run (e.g. `testOnly *FunctionArguments*`).
 
-#### Building the Updater Native Binary
-
-Then, you can build the updater/launcher using:
-
-```bash
-sbt launcher/buildNativeImage
-```
-
 #### Passing Debug Options
 
 GraalVM provides some useful debugging options, including the ability to output
@@ -368,132 +375,44 @@ please choose one best suited for you.
 
 Once you have a copy of the dynamic library, it needs to be placed in
 `$JVM_HOME/lib/server`.
+-->
 
-#### Native Image
+### Running Enso from CLI
 
-Native image is a capability provided alongside GraalVM that allows the
-generation of native executables from JVM language programs (such as the Enso
-interpreter itself). However, it results in significantly degraded peak
-performance, so it is not part of our roadmap currently.
-
-If you would like to experiment with it, you can execute the `buildNativeImage`
-command in the sbt shell while inside the `runner` project. Please note that
-while the command is available at the moment, and you are welcome to
-[report an issue](https://github.com/enso-org/enso/issues/new?assignees=&labels=Type%3A+Bug&template=bug-report.md&title=)
-with the functionality, any bugs you report will _not_ be considered high
-priority.
-
-**WE CURRENTLY DO NOT SUPPORT THE NATIVE IMAGE BUILD.**
-
-#### Using IntelliJ
-
-Internally, most of the developers working on the Enso project use IntelliJ as
-their primary IDE. To that end, what follows is a basic set of instructions for
-getting the project into a working state in IntelliJ.
-
-1. Clone the project sources.
-2. Open IntelliJ
-3. File -> New -> Project From Existing Sources.
-4. Navigate to the directory into which you cloned the project sources. By
-   default this will be called `enso`. Select the directory, and not the
-   `build.sbt` file it contains.
-5. In the 'Import Project' dialogue, select 'Import project from external model'
-   and choose 'sbt'.
-6. Where it says 'Download:', ensure you check both 'Library Sources' and 'sbt
-   sources'.
-7. In addition, check the boxes next to 'Use sbt shell:' such that it is used
-   both 'for imports' and 'for builds'.
-8. Disallow the overriding of the sbt version.
-9. Under the 'Project JDK' setting, please ensure that it is set up to use a
-   GraalVM version as described in [System requirements](#system-requirements).
-   You may need to add it using the 'New' button if it isn't already set up.
-10. Click 'Finish'. This will prompt you as to whether you want to overwrite the
-    `project` folder. Select 'Yes' to continue. The Enso project will load up
-    with an open SBT shell, which can be interacted with as described above. You
-    will want to use scalafmt for formatting of Scala code, and install Google
-    Java Format for formatting Java code. For more information see the relevant
-    [Style Guides](style-guide/README.md).
-
-Depending on the version of GraalVM with which you are working, you may be
-required to add the following flags to the per-module overrides for IntelliJ's
-java compiler in order for it to not show spurious errors. This is because some
-versions of GraalVM export their own closed version of `com.oracle.truffle.api`
-that IntelliJ picks up preferentially to the version we use for development. You
-can find these options in
-`Preferences -> Build, Execution, Deployment -> Compiler -> Java Compiler`.
-
-```
---add-exports org.graalvm.truffle/com.oracle.truffle.api=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.debug=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.dsl=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.exception=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.frame=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.instrumentation=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.interop=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.io=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.library=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.memory=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.nodes=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.object=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.profiles=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.source=ALL-UNNAMED
---add-exports org.graalvm.truffle/com.oracle.truffle.api.utilities=ALL-UNNAMED
-```
-
-However, as mentioned in the [Troubleshooting](#troubleshooting) section below,
-the forked nature of execution in the SBT shell means that we can't trivially
-make use of the IntelliJ debugger. In order to get debugging working, you will
-need to follow these steps:
-
-1. Go to Run -> Edit Configurations.
-2. Click the `+` button in the header of the 'Run/Debug Configurations' dialogue
-   that pops up.
-3. Select 'Remote' and name the new configuration appropriately.
-4. In the options for that configuration select 'Listen to remote JVM' under
-   'Debugger mode:'
-5. Where it provides the command-line arguments for the remote JVM, copy these
-   and add them to `truffleRunOptions` in [`build.sbt`](build.sbt). Remove the
-   portion of these options after `suspend=y`, including the comma. They are
-   placeholders that we don't use.
-6. Alternatively, certain tasks, such as `run`, `benchOnly` and `testOnly` can
-   be used through the `withDebug` SBT command. For this to work, your remote
-   configuration must specify the host of `localhost` and the port `5005`. The
-   command syntax is `withDebug --debugger TASK_NAME -- TASK_PARAMETERS`, e.g.
-   `withDebug --debugger testOnly -- *AtomConstructors*`.
-7. Now, when you want to debug something, you can place a breakpoint as usual in
-   IntelliJ, and then execute your remote debugging configuration. Now, in the
-   SBT shell, run a command to execute the code you want to debug (e.g.
-   `testOnly *CurryingTest*`). This will open the standard debugger interface
-   and will allow you to step through your code.
-
-**Please be careful** to ensure that you don't commit these changes to the sbt
-configuration as they are specific to your machine.
-
-#### Troubleshooting
-
-If you are having issues building Enso, please check the list below before
-filing an issue with us.
-
-- **`StackOverflowError` During Compilation:** Please ensure that your version
-  of sbt is respecting the project's `.jvmopts` settings. We make significant
-  use of recursion when expanding macros for the parser, and these require use
-  of additional stack. Alternatively, you can explicitly pass `-Xss8M` to the
-  `sbt` invocation.
-- **Debugging Not Working:** The sbt tasks run the invoked programs in a forked
-  JVM. This means that to attach a debugger to it you need to use the JVM remote
-  debugging support. Follow [Enso debugging instructions](debugger/README.md) or
-  see the [Using IntelliJ](#using-intellj) section for instructions.
-
-If your problem was not listed above, please
-[file a bug report](https://github.com/enso-org/enso/issues/new?assignees=&labels=Type%3A+Bug&template=bug-report.md&title=)
-in our issue tracker and we will get back to you as soon as possible.
-
-### Testing Enso
-
-Running the tests for the JVM enso components is as simple as running
-`sbt / test`. To test the Rust components you can run `./run wasm test`.
-
-#### Testing Enso Libraries
+The language interpreter CLI distribution can be
+[built by `sbt`](#building-enso-engine). To _execute the Enso interpreter_ use
+`sbt` as well:
+
+```sbt
+$ sbt
+sbt:enso> runEngineDistribution --help
+```
+
+The `runEngineDistribution` task takes care of everything. It builds the
+distribution in the `built-distribution` directory (with the help of
+`buildEngineDistribution` command) and launches the generated Enso launcher
+executable.
+
+Detailed information on the flags it supports can be shown with the `--help`
+flag, but the primary functionality is as follows:
+
+- `--new PATH`: Creates a new Enso project at the location specified by `PATH`.
+- `--run PATH`: Executes the interpreter on the Enso source specified by `PATH`.
+  In this case, `PATH` must point to either a standalone Enso file or an Enso
+  project.
+
+```sbt
+sbt:enso> runEngineDistribution --new Hello
+```
+
+The above command generates project in directory `Hello` with the main source
+file being at `Hello/src/Main.enso`. One can edit the file and then execute it:
+
+```sbt
+sbt:enso> runEngineDistribution --run Hello
+```
+
+### Testing Enso Libraries
 
 To run the tests inside sbt you can use the following command:
 
@@ -516,6 +435,15 @@ Or you can pattern match against the test name using this syntax
 sbt:enso> runEngineDistribution --run test/Base_Tests/src/Data/Time/Duration_Spec.enso should.normalize
 ```
 
+Or even control additional _environment variables_ of the running process. The
+Base tests rely in a few places on the system language controlled (on Linux) by
+a value of `LANG` environment variable. To change value of `LANG` environment
+variable to `C` run the tests with `--env` option and name/value pair:
+
+```sbt
+sbt:enso> runEngineDistribution --env LANG=C --run test/BASE_Tests
+```
+
 This runs all tests in Duration_Spec.enso that have 'should normalize' the their
 name.
 
@@ -526,8 +454,14 @@ breakpoint in a test then run with
 sbt:enso> runEngineDistribution --run test/Base_Tests --debug
 ```
 
-The above running options also work when debugging.
+Then connect your development environment to the 5005 port. More
+[details on debugging](./debugger/README.md) are available in a separate
+document.
 
+When using the `runEngineDistribution` command of `sbt` the Java assertions
+(`-ea` JVM option) as well as Enso assertions are enabled.
+
+<!--
 Alternatively to run the test outisde of sbt you need to first build the engine,
 the easiest way to do so is to run `sbt buildEngineDistributionNoIndex`. That
 will create a distribution in the directory `built-distribution`. The engine
@@ -580,84 +514,44 @@ Note that JVM assertions are not enabled by default, one has to pass `-ea` via
 (method `Runtime.assert`) that can be enabled when `ENSO_ENABLE_ASSERTIONS`
 environment variable is set to "true". If JVM assertions are enable, Enso
 assertions are enabled as well.
+-->
 
-#### Test Dependencies
-
-To run all the stdlib test suites, set `CI=true` environment variable:
-
-```bash
-env CI=true enso --run test/Base_Tests/
-```
-
-For more details about the CI setup, you can check the
-`.github/workflows/scala.yml` GitHub workflow.
-
-### Running Enso
-
-The language interpreter can be started by the `bin/enso` launcher script
-located inside of the Enso runtime distribution. Use the following `sbt` command
-to compile necessary bits (see
-[Building the Interperter CLI Fat Jar](#building-the-interpreter-cli-fat-jar))
-and generate the Enso distribution:
-
-##### Bash
-
-```bash
-$ sbt buildEngineDistribution
-$ sbt runEngineDistribution --help
-```
-
-Engine package created at
-built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev - use it or
-the `sbt runEngineDistribution` command to invoke Enso.
+#### Static Analysis
 
-##### PowerShell
+Additionally, you can check a project/script for basic errors (without running
+it) using `--compile` flag. The `--enable-static-analysis` flag enables
+experimental static analysis passes. So if you want to try out the type checker
+prototype on your project, you can run:
 
-```powershell
-sbt.bat buildEngineDistribution
-sbt.bat runEngineDistribution --help
+```sbt
+sbt:enso> runEngineDistribution --compile Hello --enable-static-analysis
 ```
 
-One can use the `runEngineDistribution` command or execute the launcher:
-
-```bash
-$ built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso
-```
+#### Native Image
 
-Detailed information on the flags it supports can be shown with the `--help`
-flag, but the primary functionality is as follows:
+Native image is a capability provided alongside GraalVM that allows the
+generation of native executables from JVM language programs (such as the Enso
+interpreter itself). Details:
 
-- `--new PATH`: Creates a new Enso project at the location specified by `PATH`.
-- `--run PATH`: Executes the interpreter on the Enso source specified by `PATH`.
-  In this case, `PATH` must point to either a standalone Enso file or an Enso
-  project.
+- [infrastructure](./infrastructure/native-image.md)
+- [native image debugging](./debugger/native-debugging.md)
+- [sbt support](./infrastructure/sbt.md)
 
-##### Bash
+Native image support is disabled for development (as it slows down
+edit/compile/run cycle significantly). One has to **opt-in** to enable it.
 
-```bash
-distribution/bin/enso --new ~/Hello
-distribution/bin/enso --run ~/Hello
-Hello, World!
-```
+<!--
+#### Test Dependencies
 
-##### PowerShell
+To run all the stdlib test suites, set `CI=true` environment variable:
 
 ```bash
-distribution/bin/enso.bat --new ~/Hello
-distribution/bin/enso.bat --run ~/Hello
-Hello, World!
+env CI=true enso --run test/Base_Tests/
 ```
 
-Additionally, you can check a project/script for basic errors (without running
-it) using `--compile` flag. The `--enable-static-analysis` flag enables
-experimental static analysis passes. So if you want to try out the type checker
-prototype on your project, you can run:
-
-##### Bash
-
-```bash
-sbt runEngineDistribution --compile ~/Hello --enable-static-analysis
-```
+For more details about the CI setup, you can check the
+`.github/workflows/scala.yml` GitHub workflow.
+-->
 
 #### Running IDE
 
diff --git a/docs/infrastructure/native-image.md b/docs/infrastructure/native-image.md
index 146675dc6361..b0b5a82a8efa 100644
--- a/docs/infrastructure/native-image.md
+++ b/docs/infrastructure/native-image.md
@@ -11,7 +11,8 @@ order: 3
 [`NativeImage`](../../project/NativeImage.scala) defines a task that is used for
 compiling a project into a native binary using Graal's Native Image. It compiles
 the project and runs the Native Image tool which builds the image. Currently,
-Native Image is used for building the Launcher.
+Native Image is used for building the `ensoup` launcher, `project-manager` and
+also `enso` executable once one **opts-in** to it via `ENSO_LAUNCHER` option.
 
 <!-- MarkdownTOC levels="2,3" autolink="true" -->
 
diff --git a/project/DistributionPackage.scala b/project/DistributionPackage.scala
index ca6634e5fa80..2ac25fac82ee 100644
--- a/project/DistributionPackage.scala
+++ b/project/DistributionPackage.scala
@@ -332,14 +332,25 @@ object DistributionPackage {
     jvmOptName: String,
     envToFill: java.util.Map[String, String]
   ): Unit = {
+    var atEnv = args.indexOf("--env")
+    while (atEnv >= 0) {
+      var keyAndValue = args.get(atEnv + 1).split("=")
+      envToFill.put(keyAndValue(0), keyAndValue(1))
+      args.remove(atEnv)
+      args.remove(atEnv)
+      atEnv = args.indexOf("--env")
+    }
+
     var prevValue = System.getenv(jvmOptName)
     if (prevValue == null) {
       prevValue = "-ea";
+    } else {
+      prevValue = prevValue + " -ea"
     }
 
     val at = args.indexOf("--debug")
     if (at >= 0) {
-      args.set(at, "--jvm")
+      args.set(at, "--jvm=" + System.getProperty("java.home"))
       val newValue =
         prevValue + " " + WithDebugCommand.DEBUG_OPTION
       envToFill.put(jvmOptName, newValue)
diff --git a/project/EnsoProjects.scala b/project/EnsoProjects.scala
index 7ca8d1464f6f..46ffeb1e99d3 100644
--- a/project/EnsoProjects.scala
+++ b/project/EnsoProjects.scala
@@ -10,9 +10,10 @@ import java.nio.file.Path
 object EnsoProjects {
   case class Project(namespace: Option[String], name: String, path: Path) {
     def usesPrivateAccess: Boolean =
-      path.getParent.getFileName.toString == "test" && name.contains(
-        "_Internal_"
-      ) && name.endsWith("_Tests")
+      path.getParent != null && path.getParent.getFileName.toString == "test" && name
+        .contains(
+          "_Internal_"
+        ) && name.endsWith("_Tests")
   }
 
   def ofPath(path: Path): Project =


Reviews:
user: login: AdRiley
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T07:29:26Z
user: login: Akirathan
body: OK. You can delete the already outdated section about IJ, but please keep the single sentence I provided.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T07:48:01Z
user: login: AdRiley
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-21T08:16:26Z
user: login: JaroslavTulach
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T10:47:14Z

Review comments:
diff_hunk: @@ -23,12 +23,14 @@ sections of this document are linked below:
   - [Design Documentation](#design-documentation)
   - [System Requirements](#system-requirements)
   - [Getting the Sources](#getting-the-sources)
-  - [Getting Set Up \(Rust\)](#getting-set-up-rust)
-  - [Getting Set Up \(JVM\)](#getting-set-up-jvm)
-  - [Getting Set Up \(Documentation\)](#getting-set-up-documentation)
-  - [Building Enso](#building-enso)
-  - [Testing Enso](#testing-enso)
-  - [Running Enso](#running-enso)
+  - Configuration
+    - [Set Up Rust)](#getting-set-up-rust)
path: docs/CONTRIBUTING.md
user: login: AdRiley
body: ```suggestion
    - [Set Up Rust](#getting-set-up-rust)
```
created_at: 2025-05-21T07:29:25Z
author_association: MEMBER
diff_hunk: @@ -238,6 +240,115 @@ git clone https://github.com/enso-org/enso.git
 git clone git@github.com:enso-org/enso.git
 ```
 
+The recommended IDE for working with the sources is
+[VSCode with Enso extension](../tools/enso4igv/README.md).
path: docs/CONTRIBUTING.md
user: login: Akirathan
body: "Alternativelly, use the IntelliJ with the official [Scala plugin](https://plugins.jetbrains.com/plugin/1347-scala) and import the sbt project". This is sufficient to mention for IJ. You can just delete the section under `<!--`, it is outdated anywhere.
created_at: 2025-05-21T07:45:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -238,6 +240,115 @@ git clone https://github.com/enso-org/enso.git
 git clone git@github.com:enso-org/enso.git
 ```
 
+The recommended IDE for working with the sources is
+[VSCode with Enso extension](../tools/enso4igv/README.md).
path: docs/CONTRIBUTING.md
user: login: JaroslavTulach
body: Thanks. Done in a1b2c99983
created_at: 2025-05-21T10:47:14Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-21T10:45:45Z
body: ## ✨ GUI Checks Results


### Summary
- 🧹 Prettier: ✅ Passed
- 🧰 GUI Checks: ❌ Failed
- 📚 Storybook: ❌ Failed

> ⚠️ **Action Required**: Please review the failed checks and fix them before merging.

See individual check results for more details.
author_association: NONE
user: login: JaroslavTulach
created_at: 2025-05-21T14:40:14Z
body: The `license/claExpected — Waiting for status to be reported` is stuck. This is a hint I got from @AdRiley:

> I've found a cool thing.
As you know, sometimes the CLA status check gets stuck on Expected - Waiting for status to be reported and it can be stuck like that indefinitely. Usually the only way around this is to push an empty commit (restarting ALL checks which is tediously slow) or asking an admin to force merge. Quite annoying if this is the only reason stopping the PR from getting merged.
>
> Not anymore!
Turns out you just need to fetch https://cla-assistant.io/check/enso-org/enso?pullRequest=<pr-number> and this should re-trigger the CLA check. So it's a way of getting a PR waiting on CLA unstuck without any forces or retriggering the whole pipeline.

Let's try it!

<blockquote><img src="https://github.githubassets.com/assets/home24-5939032587c9.jpg" width="48" align="right"><div><img src="https://github.githubassets.com/favicons/favicon.svg" height="14"> GitHub</div><div><strong><a href="https://github.com/">GitHub · Build and ship software on a single, collaborative platform</a></strong></div><div>Join the world's most widely adopted, AI-powered developer platform where millions of developers, businesses, and the largest open source community build software that advances humanity.</div></blockquote>
author_association: MEMBER