Skip to content
This repository has been archived by the owner on May 18, 2023. It is now read-only.

Fix/local mira #96

Merged
merged 14 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ cdk.out/
node_modules/
dist/
docs/
bin/
bin/
scripts/link.js
31 changes: 31 additions & 0 deletions docs/quick-start/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,37 @@ Clone your newly created repository and follow the instructions below.
__Note:__ The default tags will be associated to that stack: StackName, CreatedBy (Owner) and CostCenter (If defined).
At this step you should have your development environment deployed and ready to use.

## Developing with Mira locally

You may want to author changes to the Mira package and immediately use those
changes in your Mira application. You can do this using the following syntax
from your local Mira directory:

```
node scripts/link.js [APPLICATION_PATH]
```

For instance, if your application is at `../app` you'd run the script:

```
node scripts/link.js ../app
```

If you have an existing installation or Mira dependency installed in `../app`
then Mira will copy it to `../app/mira.old`. To reinstall a remote version of
Mira from GitHub or NPM just delete your `node_modules` folder and re-run
`npm install` in your application directory.

### Implementation Details

The reason Mira uses the `link` script found under `scripts/link.js` is because
of complications arising from AWS CDK requiring itself to be listed as a
peer dependency. Utilizing the `--preserve-symlinks` flag alongside `npm link`
usage is not enough: Mira will still read `app/node_modules/mira/node_modules/aws-cdk`.

As a result of this, Mira implements the `link` script which will symlink in the
necessary files and dependencies to run Mira without symlinking the AWS CDK
dependencies intended to be used as peer dependencies.

## Continuous Integration

Expand Down
15 changes: 15 additions & 0 deletions scripts/bin/mira
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

case `uname` in
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../mira/bin/cli.js" "$@"
ret=$?
else
node "$basedir/../mira/bin/cli.js" "$@"
ret=$?
fi
exit $ret
17 changes: 17 additions & 0 deletions scripts/bin/mira.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@ECHO off
SETLOCAL
CALL :find_dp0

IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)

"%_prog%" "%dp0%\..\mira\bin\cli.js" %*
ENDLOCAL
EXIT /b %errorlevel%
:find_dp0
SET dp0=%~dp0
EXIT /b
18 changes: 18 additions & 0 deletions scripts/bin/mira.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent

$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
& "$basedir/node$exe" "$basedir/../mira/bin/cli.js" $args
$ret=$LASTEXITCODE
} else {
& "node$exe" "$basedir/../mira/bin/cli.js" $args
$ret=$LASTEXITCODE
}
exit $ret
141 changes: 141 additions & 0 deletions scripts/link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
const assert = require('assert')
const minimist = require('minimist')
const args = minimist(process.argv.slice(2))
const cp = require('child_process')
const fs = require('fs')

const excludeList = [
'@aws-cdk',
'aws-cdk'
]

/**
* Creates the bootstrap files for Mira.
*/
const createMiraBootstrap = (pkgDir) => {
try { fs.mkdirSync(`${pkgDir}/node_modules/mira-bootstrap`) } catch (err) {
// NOOP
}

fs.writeFileSync(`${pkgDir}/node_modules/mira-bootstrap/cli.js`,
'require(\'mira/bin/cli\');', 'utf8')

fs.readdirSync(__dirname + '/bin').forEach((file) => {
fs.symlinkSync(`${__dirname}/bin/${file}`, `${pkgDir}/node_modules/.bin/${file}`)
})
}

/**
* Normalize a pathname.
*/
const normalize = (path) => {
// TODO: Enable the mustExist variable.
path = require('path').resolve(path).replace(/\\/g, '/')
if (process.platform === 'win32') {
return path.toLowerCase()
} else {
return path
}
}

/**
* Gets the package.json given a file path. If not found in the filepath
* directory, moves up a directory.
* @param {String} file The file path to search from.
* @return {Boolean|String} False if no file is found.
*/
const getPackageFile = (path) => {
if (!path) {
path = process.cwd()
}

if (!require('fs').existsSync(path)) {
return false
}

try {
path = normalize(path) // Why would this fail?
} catch (e) {
try {
if (require('fs').existsSync(process.cwd() + '/' + path)) {
path = normalize(process.cwd() + '/' + path)
}
} catch (e) {
return false
}
}

if (!require('fs').statSync(path).isDirectory()) {
path = require('path').dirname(path)
}
while (!require('fs').existsSync(path + '/package.json') &&
path.length > 1 && path.split('/').length > 1 &&
path.split('/')[1].length > 0) {
path = path.split('/').slice(0, -1).join('/')
}
if (require('fs').existsSync(path + '/package.json')) {
return path + '/package.json'
} else {
return ''
}
}

/**
* Symlinks the `node_modules` into the target application directory's Mira dep.
*
* This excludes the packages that are intended as peer dependencies within
* Mira itself.
*/
const symlinkDependencies = (pkgDir) => {
try { fs.mkdirSync(`${pkgDir}/node_modules/mira/node_modules`) } catch (err) {
// NOOP
}
const dependencies = fs.readdirSync(__dirname + '/../node_modules/')
dependencies.forEach((dep) => {
if (excludeList.includes(dep)) {
return
}
fs.symlinkSync(`${__dirname}/../node_modules/${dep}`,
`${pkgDir}/node_modules/mira/node_modules/${dep}`)
})
}

/**
* Symlinks Mira into the the application directory.
*/
const symlinkMira = (pkgDir) => {
try { fs.mkdirSync(`${pkgDir}/node_modules/mira`) } catch (err) {
// NOOP
}
fs.symlinkSync(__dirname + '/../dist/', `${pkgDir}/node_modules/mira/dist`)
fs.symlinkSync(__dirname + '/../bin/', `${pkgDir}/node_modules/mira/bin`)
fs.symlinkSync(__dirname + '/../package.json', `${pkgDir}/node_modules/mira/package.json`)
}

if (getPackageFile(args._[0])) {
const pkgPath = getPackageFile(args._[0])
const pkgDir = pkgPath.split(/\//g).slice(0, -1).join('/')
const pkgObj = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
assert(pkgObj.name, 'Looks like the relevant package object found in the ' +
'path you specified does not define a name field.')
if (!fs.existsSync(`${pkgDir}/node_modules`)) {
console.warn(`Looks like you have not installed the module ${pkgObj.name}:\n` +
`Path: ${pkgDir}`)
}
if (fs.existsSync(`${pkgDir}/node_modules/mira`) &&
!fs.statSync(`${pkgDir}/node_modules/mira`).isSymbolicLink()) {
console.info(`Looks like Mira has already been installed in ${pkgObj.name}`)
console.info(`Backing up old mira dependency to ${pkgDir}/mira.old`)
if (fs.existsSync(`${pkgDir}/mira.old`)) {
console.warn('mira.old folder already exists, deleting.')
cp.execSync(`rm -rf ${pkgDir}/mira.old`, {
stdio: 'ignore'
})
}
fs.renameSync(`${pkgDir}/node_modules/mira`, `${pkgDir}/mira.old`)
}
symlinkMira(pkgDir)
symlinkDependencies(pkgDir)

createMiraBootstrap(pkgDir)
}
16 changes: 14 additions & 2 deletions src/cdk/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from 'path'
import fs from 'fs'
import chalk from 'chalk'
import { ParsedArgs } from 'minimist'
import { spawn } from 'child_process'
Expand Down Expand Up @@ -236,8 +237,19 @@ export class MiraBootstrap {
getCDKArgs (filename: string, isCi = false, env?: string): string {
const resultedEnv = this.env || env
const q = process.platform === 'win32' ? '"' : '\''
const appPath = path.resolve(__dirname, filename)
let appArg = `${q}node "${appPath}" `
let appPath = path.resolve(__dirname, filename)
if (fs.existsSync('node_modules/mira')) {
if (fs.lstatSync('node_modules/mira/dist').isSymbolicLink()) {
// Mira has been locally linked.
try { fs.mkdirSync('node_modules/mira-bootstrap') } catch (e) {
// NOOP
}
fs.writeFileSync('node_modules/mira-bootstrap/bootstrap-app.js',
'require(\'mira/dist/src/cdk/app.js\')', 'utf8')
appPath = 'node_modules/mira-bootstrap/bootstrap-app.js'
}
}
let appArg = `${q}node --preserve-symlinks "${appPath}" `
// Still inside the quotes, explode the args.
// appArg += this.getArgs().join(' ')
appArg += this.stackFile ? ` --file=${this.stackFile}` : ''
Expand Down