--- tags: v3, infrastructure, ops, guide --- # Infrastructure Guide Walkthrough (9/21/22) - We should keep *consumers* of the guide in mind as we build this out ## Monorepo Setup Our monorepo is scaffolded with `nx`. The [nx documentation](https://nx.dev/) has a lot of useful content if you're looking for a more general overview. This section contains information for key files and commands that we utilize in our monorepo setup. ### Key nx Monorepo Files Let's take a look at key files used in the `nx` monorepo setup. #### `workspace.json` - Maps to all of the projects - This is how`nx` knows what to reference when we run commands such as `nx run ui:build` - The project name coming after `nx rn` needs to be included in this list, and the command after the colon must exist in that project's config! - Referenced in each project's`tsconfig` as well - When generating a new app or library using our generators these are updated #### `nx.json` - Global configuration for `nx` - Don't need to touch this very often, but we did customize the `targetDefaults`: ```json "targetDefaults": { "build": { "dependsOn": ["^build", "tsc"] } }, ``` - When running `build` this also runs `tsc` so that we include TypeScript checks (`tsc`) since `vite` doesn't do this by default ### Key App and Library Files Let's dive a step deeper and look at key files in each project (apps and libraries). #### `project.json` Each project has a `project.json` generated at the root. This is a similar to the scripts section of a `package.json` file. It defines the commands and what they're executing. This is typically a plugin's executor. Let's look at an example from the Hub app package: ```json "targets": { "build": { "executor": "@nxext/vite:build", ``` - In this example, the Hub`build` command targets the `@nxext/vite:build` plugin's `build` executor and runs that when running `nx run hub-app:build` - For a deeper understanding into what's going on behind the scenes, you can go to each plugin's code on GitHub and find the `executors.json` file which define the arguments for the executor - These args are where we get the options used in the `project.json We utilize `dependsOn` and `configurations` in certain project's `project.json`s. Let's take a look at an example from the `ui` library. ``` "build": { "outputs": ["{options.outputPath}"], "executor": "@nrwl/workspace:run-commands", "options": { "outputPath": "dist/libs/ui", "command": "tsc --project libs/ui/tsconfig.lib.json --emitDeclarationOnly --skipLibCheck", "color": true }, "dependsOn": [ { "target": "vite-build", "projects": "self" }, { "target": "build-storybook", "projects": "self" } ] }, ``` - `dependsOn` - Can stack commands on top of each other - In the example from the `ui` library, in order to run `build`, `vite-build` and `build-storybook` need to run first - Any commands listed in the `dependsOn` array need to run successfully *first* before the project's `build` command will run ``` "build-storybook": { "executor": "@nrwl/storybook:build", "outputs": ["{options.outputPath}"], "inputs": [{ "env": "NODE_ENV" }], "options": { "uiFramework": "@storybook/react", "outputPath": "dist/storybook/ui", "config": { "configFolder": "libs/ui/.storybook" }, "quiet": false }, "configurations": { "ci": { "quiet": true } } }, ``` - `configurations` - We don't currently leverage this much, but this adds a subcommand (in the example, within `storybook`) - We use this in the `build-storybook` comamnd in the `project.json` from the `ui` library - `nx run ui: build-storybook:ci` - Inheret all the config and also overwrite the `quiet` config value in the initial build command - Can add configurations within each command -- such as subgraph parameters - Rinkeby as the key and then the options for the specific blockchain - We'd want to chain the commands (such as `build`) that are used in our CI flows ## CI Actions Let's look deeper at our CI workflows. These are `.yaml` files located in the `.github` folder in the monorepo root. Each action has a different name that corresponds to the branch. For example, `ci_develop.yaml` is named *CI Develop*. Let's look at the *CI Develop* action - The *CI Develop* action is set to run on pushes directly to the `develop` branch and on PRs *into* `develop` - Once the action is triggered, it creates a `.env` for the build. This includes a lot of key information: - `BASE` branch: ``` BASE: ${{ github.ref == 'refs/heads/develop' && 'origin/develop~1' || 'origin/develop' }} ``` - This sets the action ref as `develop` and includes any pushes directly to develop as well as PRs into `develop` (`origin/develop~1`) -- this is "1` commit below the head" - If this isn't included the action wouldn't run on PRs into develop - This structure enables our build action to run if we need to make a quick PR into `develop` (even though we don't typically do this) AND on PRs into `develop` (our current workflow) - The `master` workflow is set up very similarly to run on merges directly into the branch for hotfixes as well as for our workflow of merging `develop` into `master` - `nx affected:command` - If your code has changed relative to the base branch (`BASE`), then it'll run the connected command for each step - Every app/library should have a `lint`, `test`, and `build` command since these are used in our CI - If the app/library doesn't have a command of the same name, it won't run when `affected` runs -- name must be the same! - If the name is even slightly off, such as `linter` instead of `lint` it won't run in the CI step - Test this with the generate commands to make sure that these exist - If any of these commands aren't supported we'd want to fork and customize for DH needs - Would be good to enforce at the code level than relying on maintaining docs about it - The [nx docs](https://nx.dev/concepts/affected) have additional information about how `affected` works ### Secrets and Environment Variables When the actions run, they create a `.env` with the environment variables we need and are configured to use ones stored within our GitHub Secrets. - These are prefixed with `secrets.` -- this indicates that the secrets are safely stored in our GitHub Secrets - It's critical to make sure that when we're adding new environment variables that we add them to the GitHub Secrets and to the Actions `yaml` file. If we don't do this, the `env` variables will be `undefined` at build and runtime! ## Deployments and Services We use [Skynet Labs](https://skynetlabs.com/) for decentralized hosting of our apps (Hub, Summoner, and Admin). Sia is like IPFS (where the files are pinned to) and Skynet is similar to Infura or Pinata. Skynet is a *separate* service that we're using. - Currently, pushes to `develop` trigger our build and deployment workflow whereas pushes to `master` are utilized for publishing to `npm` - Our Actions *build* our apps and then deploy directly to Skynet for hosting - Each time the build successfully runs there will be a corresponding entry in the *Uploads* list on the Skynet Dashboard - *Name* is the app name, such as `hub-app` - *Skylink* is a hash of the file itself - Every time the build files change (which is upon a successful build) there will be a new hash for that app - The **total storage** we're allowed is configured across *all apps* connected with our account key, not on a per app basis ### Skynet Portals and DNS - Skynet contains *portals*, which are entry points into the network - We currently use siasky.net (this is a public portal) - We may need to switch to a different portal depending on the upcoming changes to Skynet Labs due to [their recent sustainability announcement](https://www.coindesk.com/business/2022/08/12/blockchain-firm-skynet-labs-shutters-after-failing-to-get-new-funding/) in August 2022 - For DNS, there is a [useful tutorial](https://docs.skynetlabs.com/integrations/dnslink-and-domain-names) in the Skynet Docs ## Troubleshooting - Refer to [`vite` docs](https://vitejs.dev/config/) for frontend packaging and configuration explanations - Forking and modifying `nx` generators is a helpful approach for deepending understanding - Recognize when we're going down a bad rabbithole: - Indicators would be heavy changes to the vite config - Adding a bunch of polyfills to solve problems - Heavy modifications to the `package.json` outside of the defaults - Try to keep as much in the monorepo as possible - Try to reproduce issues locally when troubleshooting building ### Running Builds Locally - You can test out the `build` command locally by running `nx run project-name:build` (such as `nx run hub-app:build`) - You can also use this to test individual steps in the build such as `nx run ui:build-storybook` - If you're debugging errors on a *deployed app* you can test this locally beyond running the `build` command with the following steps (using Hub as an example): - Run the associated `build` command: `nx run hub-app:build` - Navigate into the output folder (default is `dist` unless changed in the configuration) - Serve this folder directly with an HTTP client and navigate to the provided URL - This can be done in VSCode by opening the `hub-app` found in the `dist` folder and then running VSCode's Live Server and heading to the served URL - This is essentially the process that the deployment workflow uses so you're able to troubleshoot for errors that may be occurring *after* build and deployment - Going through this process is helpful to debug and catch issues happening after build and deployment