Skip to content

Commit

Permalink
Adds wasp deploy CLI command for Fly.io (#961)
Browse files Browse the repository at this point in the history
  • Loading branch information
shayneczyzewski authored Feb 7, 2023
1 parent 979904d commit b14ed94
Show file tree
Hide file tree
Showing 41 changed files with 4,478 additions and 20 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ jobs:
if: matrix.os == 'ubuntu-20.04'
run: ./run ormolu:check

- name: Compile deploy TS package and move it into the Cabal data dir
if: matrix.os == 'ubuntu-20.04' || matrix.os == 'macos-latest'
run: ./tools/install_deploy_package_to_data_dir.sh

- name: Build external dependencies
run: cabal build --enable-tests --enable-benchmarks --only-dependencies

Expand Down
3 changes: 3 additions & 0 deletions waspc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## v0.8.1

### `wasp deploy` CLI command added
We have made it much easier to deploy your Wasp apps via a new CLI command, `wasp deploy`. 🚀 This release adds support for Fly.io, but we hope to add more hosting providers soon!

### Import Wasp entity types on the backend
You can now import and use the types of Wasp entities in your backend code:
```typescript
Expand Down
4 changes: 4 additions & 0 deletions waspc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ Tip: to make it easy to run the `run` script from any place in your wasp codebas
alias wrun="/home/martin/git/wasp-lang/wasp/waspc/run"
```

### Typescript packages
Wasp bundles some TypeScript packages into the installation artifact (eg: deployment scripts), which end up in the installed version's `waspc_datadir`. To do so in CI, it runs `./tools/install_deploy_package_to_data_dir.sh`.

During normal local development you can treat `packages/deploy` as a regular TS project and develop against it in a standalone manner. However, if you want to test it as part of the Wasp CLI, you can make use of this same script locally. Just manually invoke it before you run something like `cabal run wasp-cli deploy fly ...` in a wasp project so the local data directory is up to date.

## Tests
For tests we are using [**Tasty**](https://github.com/UnkindPartition/tasty) testing framework. Tasty let's us combine different types of tests into a single test suite.
Expand Down
4 changes: 4 additions & 0 deletions waspc/cli/exe/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Wasp.Cli.Command.Compile (compile)
import Wasp.Cli.Command.CreateNewProject (createNewProject)
import Wasp.Cli.Command.Db (runDbCommand, studio)
import qualified Wasp.Cli.Command.Db.Migrate as Command.Db.Migrate
import Wasp.Cli.Command.Deploy (deploy)
import Wasp.Cli.Command.Deps (deps)
import Wasp.Cli.Command.Dockerfile (printDockerfile)
import Wasp.Cli.Command.Info (info)
Expand Down Expand Up @@ -48,6 +49,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
["completion:generate"] -> Command.Call.GenerateBashCompletionScript
["completion:list"] -> Command.Call.BashCompletionListCommands
("waspls" : _) -> Command.Call.WaspLS
("deploy" : deployArgs) -> Command.Call.Deploy deployArgs
_ -> Command.Call.Unknown args

telemetryThread <- Async.async $ runCommand $ Telemetry.considerSendingData commandCall
Expand All @@ -70,6 +72,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
Command.Call.BashCompletionListCommands -> runCommand bashCompletion
Command.Call.Unknown _ -> printUsage
Command.Call.WaspLS -> runWaspLS
Command.Call.Deploy deployArgs -> runCommand $ deploy deployArgs

-- If sending of telemetry data is still not done 1 second since commmand finished, abort it.
-- We also make sure here to catch all errors that might get thrown and silence them.
Expand Down Expand Up @@ -101,6 +104,7 @@ printUsage =
cmd " db <db-cmd> [args] Executes a database command. Run 'wasp db' for more info.",
cmd " clean Deletes all generated code and other cached artifacts. Wasp equivalent of 'have you tried closing and opening it again?'.",
cmd " build Generates full web app code, ready for deployment. Use when deploying or ejecting.",
cmd " deploy Deploys your Wasp app to cloud hosting providers.",
cmd " telemetry Prints telemetry status.",
cmd " deps Prints the dependencies that Wasp uses in your project.",
cmd " dockerfile Prints the contents of the Wasp generated Dockerfile.",
Expand Down
1 change: 1 addition & 0 deletions waspc/cli/src/Wasp/Cli/Command/Call.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ data Call
| GenerateBashCompletionScript
| BashCompletionListCommands
| WaspLS
| Deploy [String] -- deploy cmd passthrough args
| Unknown [String] -- all args
19 changes: 19 additions & 0 deletions waspc/cli/src/Wasp/Cli/Command/Deploy.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Wasp.Cli.Command.Deploy
( deploy,
)
where

import Control.Monad.IO.Class (liftIO)
import System.Environment (getExecutablePath)
import Wasp.Cli.Command (Command)
import Wasp.Cli.Command.Common (findWaspProjectRootDirFromCwd)
import qualified Wasp.Lib as Lib

deploy :: [String] -> Command ()
deploy cmdArgs = do
waspProjectDir <- findWaspProjectRootDirFromCwd
liftIO $ do
-- `getExecutablePath` has some caveats: https://frasertweedale.github.io/blog-fp/posts/2022-05-10-improved-executable-path-queries.html
-- Once we upgrade to GHC 9.4 we should change to `executablePath`, but this should be ok for our purposes.
waspExePath <- getExecutablePath
Lib.deploy waspExePath waspProjectDir cmdArgs
17 changes: 15 additions & 2 deletions waspc/cli/src/Wasp/Cli/Command/Telemetry/Project.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import qualified Data.ByteString.Lazy.UTF8 as ByteStringLazyUTF8
import qualified Data.ByteString.UTF8 as ByteStringUTF8
import Data.Char (toLower)
import Data.Functor ((<&>))
import Data.List (intercalate, intersect)
import Data.Maybe (fromJust, fromMaybe)
import qualified Data.Time as T
import Data.Version (showVersion)
Expand All @@ -41,6 +42,7 @@ considerSendingData telemetryCacheDirPath userSignature projectHash cmdCall = do

let relevantLastCheckIn = case cmdCall of
Command.Call.Build -> _lastCheckInBuild projectCache
Command.Call.Deploy _ -> _lastCheckInDeploy projectCache
_ -> _lastCheckIn projectCache

shouldSendData <- case relevantLastCheckIn of
Expand Down Expand Up @@ -106,7 +108,8 @@ getWaspProjectPathHash = ProjectHash . take 16 . sha256 . SP.toFilePath <$> find

data ProjectTelemetryCache = ProjectTelemetryCache
{ _lastCheckIn :: Maybe T.UTCTime, -- Last time when CLI was called for this project, any command.
_lastCheckInBuild :: Maybe T.UTCTime -- Last time when CLI was called for this project, with Build command.
_lastCheckInBuild :: Maybe T.UTCTime, -- Last time when CLI was called for this project, with Build command.
_lastCheckInDeploy :: Maybe T.UTCTime -- Last time when CLI was called for this project, with Deploy command.
}
deriving (Generic, Show)

Expand All @@ -115,7 +118,7 @@ instance Aeson.ToJSON ProjectTelemetryCache
instance Aeson.FromJSON ProjectTelemetryCache

initialCache :: ProjectTelemetryCache
initialCache = ProjectTelemetryCache {_lastCheckIn = Nothing, _lastCheckInBuild = Nothing}
initialCache = ProjectTelemetryCache {_lastCheckIn = Nothing, _lastCheckInBuild = Nothing, _lastCheckInDeploy = Nothing}

-- * Project telemetry cache file.

Expand Down Expand Up @@ -155,6 +158,7 @@ data ProjectTelemetryData = ProjectTelemetryData
_waspVersion :: String,
_os :: String,
_isBuild :: Bool,
_deployCmdArgs :: String,
_context :: String
}
deriving (Show)
Expand All @@ -169,9 +173,17 @@ getProjectTelemetryData userSignature projectHash cmdCall context =
_isBuild = case cmdCall of
Command.Call.Build -> True
_ -> False,
_deployCmdArgs = case cmdCall of
Command.Call.Deploy deployCmdArgs -> intercalate ";" $ extractKeyDeployArgs deployCmdArgs
_ -> "",
_context = context
}

-- We don't really want or need to see all the things users
-- pass to the deploy script. Let's only track what we need.
extractKeyDeployArgs :: [String] -> [String]
extractKeyDeployArgs = intersect ["fly", "setup", "create-db", "deploy", "cmd"]

sendTelemetryData :: ProjectTelemetryData -> IO ()
sendTelemetryData telemetryData = do
let reqBodyJson =
Expand All @@ -188,6 +200,7 @@ sendTelemetryData telemetryData = do
"wasp_version" .= _waspVersion telemetryData,
"os" .= _os telemetryData,
"is_build" .= _isBuild telemetryData,
"deploy_cmd_args" .= _deployCmdArgs telemetryData,
"context" .= _context telemetryData
]
]
Expand Down
1 change: 1 addition & 0 deletions waspc/data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages
83 changes: 83 additions & 0 deletions waspc/packages/deploy/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
module.exports = {
"env": {
"es2020": true,
"node": true
},
"root": true,
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"eol-last": [
"error",
"always"
],
"no-multiple-empty-lines": [
"error",
{
"max": 2,
"maxEOF": 1
}
],
"comma-spacing": [
"error",
{ "before": false, "after": true }
],
"space-before-function-paren": ["error", {
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}],
"comma-dangle": [
"error",
"always-multiline"
],
"object-curly-spacing": [
"error",
"always"
],
"padding-line-between-statements": [
"error",
{ "blankLine": "always", "prev": "function", "next": "function" },
{ "blankLine": "always", "prev": "function", "next": "export" },
{ "blankLine": "always", "prev": "export", "next": "function" },
{ "blankLine": "always", "prev": "export", "next": "export" }
],
"no-duplicate-imports": "error",
"@typescript-eslint/semi": [
"error",
"always"
],
"@typescript-eslint/member-delimiter-style": [
"error",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
}
}
],
"@typescript-eslint/explicit-module-boundary-types": "error"
}
}
2 changes: 2 additions & 0 deletions waspc/packages/deploy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
Empty file.
6 changes: 6 additions & 0 deletions waspc/packages/deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
To run the deploy package as a standalone TS project, run:
```sh
npm install
npm run build
node dist/index.js fly ...
```
Loading

0 comments on commit b14ed94

Please sign in to comment.