Merge pull request #45 from Azure/chore/cleanup
style: add prettier + cleanups
This commit is contained in:
Коммит
3a6bacedff
|
@ -335,7 +335,6 @@ src/**/*.js
|
|||
!src/__mocks__/*.js
|
||||
src/**/*.js.map
|
||||
src/**/*.d.ts
|
||||
!src/@types/progress.d.ts
|
||||
lib/**/*
|
||||
|
||||
# IDEs
|
||||
|
@ -355,3 +354,4 @@ yarn.lock
|
|||
.DS_Store
|
||||
|
||||
out/
|
||||
coverage/
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
*.test.json
|
||||
__mocks__
|
||||
__tests__
|
||||
coverage/
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
module.exports = {
|
||||
"roots": [
|
||||
"<rootDir>/src"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
};
|
46
package.json
46
package.json
|
@ -11,14 +11,20 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json && npm run copy:builders:json && npm run copy:ngadd:json && tsc -p tsconfig.json",
|
||||
"start": "npm run build:watch",
|
||||
"build:watch": "npm run build -- -w",
|
||||
"build:watch": "npm run build -s -- -w",
|
||||
"format": "npm run format:check -s -- --write",
|
||||
"format:check": "prettier -l \"./src/**/*.{json,ts}\"",
|
||||
"test:jest": "jest",
|
||||
"test:jest:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"copy:builders:json": "cp ./src/builders/*.json ./out/builders",
|
||||
"copy:ngadd:json": "cp ./src/ng-add/*.json ./out/ng-add"
|
||||
},
|
||||
"keywords": [
|
||||
"schematics"
|
||||
"schematics",
|
||||
"angular",
|
||||
"azure",
|
||||
"deploy"
|
||||
],
|
||||
"author": {
|
||||
"name": "Shmuela Jacobs",
|
||||
|
@ -32,6 +38,10 @@
|
|||
{
|
||||
"name": "Chris Noring",
|
||||
"url": "https://twitter.com/chris_noring"
|
||||
},
|
||||
{
|
||||
"name": "Yohan Lasorsa",
|
||||
"url": "https://twitter.com/sinedied"
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/Azure/ng-deploy-azure/",
|
||||
|
@ -59,11 +69,8 @@
|
|||
"@azure/storage-blob": "^10.3.0",
|
||||
"adal-node": "^0.1.28",
|
||||
"chalk": "^2.4.2",
|
||||
"cli-ux": "^5.2.1",
|
||||
"conf": "^3.0.0",
|
||||
"configstore": "^4.0.0",
|
||||
"fuzzy": "^0.1.3",
|
||||
"gfycat-style-urls": "^1.0.3",
|
||||
"glob": "^7.1.3",
|
||||
"inquirer": "^6.2.2",
|
||||
"inquirer-autocomplete-prompt": "^1.0.1",
|
||||
|
@ -71,27 +78,40 @@
|
|||
"ora": "^3.4.0",
|
||||
"progress": "^2.0.3",
|
||||
"promise-limit": "^2.7.0",
|
||||
"tslib": "^1.9.3",
|
||||
"typescript": "~3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@schematics/angular": "^8.2.0",
|
||||
"@types/chai": "^4.0.4",
|
||||
"@types/conf": "^2.1.0",
|
||||
"@types/configstore": "^4.0.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/inquirer": "0.0.44",
|
||||
"@types/jasmine": "^3.3.12",
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/node": "^10.12.18",
|
||||
"jasmine": "^3.0.0",
|
||||
"@types/progress": "^2.0.3",
|
||||
"husky": "^3.0.2",
|
||||
"jest": "^24.8.0",
|
||||
"prettier": "^1.16.4",
|
||||
"prettier": "^1.18.2",
|
||||
"pretty-quick": "^1.11.1",
|
||||
"schematics-utilities": "^1.1.2",
|
||||
"ts-jest": "^24.0.2",
|
||||
"tslint-angular": "^1.1.2",
|
||||
"typescript": "~3.4.0"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
"<rootDir>/src"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
"printWidth": 120
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "pretty-quick --staged"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
declare module 'progress';
|
|
@ -24,7 +24,11 @@ import { getAccountKey } from '../../util/azure/account';
|
|||
import chalk from 'chalk';
|
||||
import { loginToAzure } from '../../util/azure/auth';
|
||||
|
||||
export default async function deploy(context: BuilderContext, projectRoot: string, azureHostingConfig?: AzureHostingConfig) {
|
||||
export default async function deploy(
|
||||
context: BuilderContext,
|
||||
projectRoot: string,
|
||||
azureHostingConfig?: AzureHostingConfig
|
||||
) {
|
||||
if (!azureHostingConfig) {
|
||||
throw new Error('Cannot find Azure hosting config for your app in azure.json');
|
||||
}
|
||||
|
@ -38,7 +42,9 @@ export default async function deploy(context: BuilderContext, projectRoot: strin
|
|||
!azureHostingConfig.app.project ||
|
||||
!azureHostingConfig.app.target
|
||||
) {
|
||||
throw new Error('Azure hosting config is missing some details. Please run "ng add ng-deploy-azure" and select a storage account.');
|
||||
throw new Error(
|
||||
'Azure hosting config is missing some details. Please run "ng add ng-deploy-azure" and select a storage account.'
|
||||
);
|
||||
}
|
||||
|
||||
const auth = await loginToAzure(context.logger);
|
||||
|
@ -52,7 +58,7 @@ export default async function deploy(context: BuilderContext, projectRoot: strin
|
|||
if (files.length === 0) {
|
||||
// build the project
|
||||
|
||||
context.logger.info(`The folder ${ azureHostingConfig.app.path } is empty.`);
|
||||
context.logger.info(`The folder ${azureHostingConfig.app.path} is empty.`);
|
||||
if (!context.target) {
|
||||
throw new Error('Cannot execute the target');
|
||||
}
|
||||
|
@ -64,7 +70,7 @@ export default async function deploy(context: BuilderContext, projectRoot: strin
|
|||
if (azureHostingConfig.app.configuration) {
|
||||
target.configuration = azureHostingConfig.app.configuration;
|
||||
}
|
||||
context.logger.info(`📦 Running "${ azureHostingConfig.app.target }" on "${ context.target.project }"`);
|
||||
context.logger.info(`📦 Running "${azureHostingConfig.app.target}" on "${context.target.project}"`);
|
||||
|
||||
const run = await context.scheduleTarget(target);
|
||||
await run.result;
|
||||
|
@ -77,35 +83,35 @@ export default async function deploy(context: BuilderContext, projectRoot: strin
|
|||
|
||||
const client = new StorageManagementClient(credentials, azureHostingConfig.azureHosting.subscription);
|
||||
const accountKey = await getAccountKey(
|
||||
azureHostingConfig.azureHosting.account, client, azureHostingConfig.azureHosting.resourceGroupName);
|
||||
|
||||
const pipeline = ServiceURL.newPipeline(
|
||||
new SharedKeyCredential(azureHostingConfig.azureHosting.account, accountKey)
|
||||
azureHostingConfig.azureHosting.account,
|
||||
client,
|
||||
azureHostingConfig.azureHosting.resourceGroupName
|
||||
);
|
||||
|
||||
const pipeline = ServiceURL.newPipeline(new SharedKeyCredential(azureHostingConfig.azureHosting.account, accountKey));
|
||||
|
||||
const serviceURL = new ServiceURL(
|
||||
`https://${ azureHostingConfig.azureHosting.account }.blob.core.windows.net`,
|
||||
`https://${azureHostingConfig.azureHosting.account}.blob.core.windows.net`,
|
||||
pipeline
|
||||
);
|
||||
|
||||
await uploadFilesToAzure(serviceURL, context, filesPath, files);
|
||||
|
||||
const accountProps = await client.storageAccounts.getProperties(
|
||||
azureHostingConfig.azureHosting.resourceGroupName, azureHostingConfig.azureHosting.account);
|
||||
azureHostingConfig.azureHosting.resourceGroupName,
|
||||
azureHostingConfig.azureHosting.account
|
||||
);
|
||||
const endpoint = accountProps.primaryEndpoints && accountProps.primaryEndpoints.web;
|
||||
|
||||
context.logger.info(
|
||||
chalk.green(`see your deployed site at ${ endpoint }`)
|
||||
);
|
||||
context.logger.info(chalk.green(`see your deployed site at ${endpoint}`));
|
||||
// TODO: log url for account at Azure portal
|
||||
}
|
||||
|
||||
function getFiles(context: BuilderContext, filesPath: string, projectRoot: string) {
|
||||
|
||||
return glob.sync(`**`, {
|
||||
ignore: ['.git', '.azez.json'],
|
||||
cwd: filesPath,
|
||||
nodir: true,
|
||||
nodir: true
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -118,10 +124,9 @@ export async function uploadFilesToAzure(
|
|||
context.logger.info('preparing static deploy');
|
||||
const containerURL = ContainerURL.fromServiceURL(serviceURL, '$web');
|
||||
|
||||
const bar = new ProgressBar(
|
||||
'[:bar] :current/:total files uploaded | :percent done | :elapseds | eta: :etas',
|
||||
{ total: files.length }
|
||||
);
|
||||
const bar = new ProgressBar('[:bar] :current/:total files uploaded | :percent done | :elapseds | eta: :etas', {
|
||||
total: files.length
|
||||
});
|
||||
|
||||
bar.tick(0);
|
||||
|
||||
|
|
|
@ -36,7 +36,11 @@ export default createBuilder<any>(
|
|||
}
|
||||
);
|
||||
|
||||
export function getAzureHostingConfig(projectRoot: string, target: string, azureConfigFile: string): AzureHostingConfig | undefined {
|
||||
export function getAzureHostingConfig(
|
||||
projectRoot: string,
|
||||
target: string,
|
||||
azureConfigFile: string
|
||||
): AzureHostingConfig | undefined {
|
||||
const azureJson: AzureJSON = JSON.parse(readFileSync(join(projectRoot, azureConfigFile), 'UTF-8'));
|
||||
const projects = azureJson.hosting;
|
||||
return projects.find(project => project.app.project === target);
|
||||
|
|
|
@ -28,8 +28,12 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
const testRunner = new SchematicTestRunner('schematics', collectionPath);
|
||||
|
||||
async function initAngularProject(): Promise<UnitTestTree> {
|
||||
const appTree = await testRunner.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions).toPromise();
|
||||
return await testRunner.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree).toPromise();
|
||||
const appTree = await testRunner
|
||||
.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions)
|
||||
.toPromise();
|
||||
return await testRunner
|
||||
.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree)
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
it('fails with a missing tree', async () => {
|
||||
|
@ -38,7 +42,7 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
|
||||
it('adds azure deploy to an existing project', async () => {
|
||||
let appTree = await initAngularProject();
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise()
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise();
|
||||
const angularJson = JSON.parse(appTree.readContent('/angular.json'));
|
||||
|
||||
expect(angularJson.projects[appOptions.name].architect.deploy).toBeDefined();
|
||||
|
@ -50,15 +54,15 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
hosting: [
|
||||
{
|
||||
app: {
|
||||
configuration: "production",
|
||||
path: "dist/test-app",
|
||||
project: "test-app",
|
||||
target: "build",
|
||||
configuration: 'production',
|
||||
path: 'dist/test-app',
|
||||
project: 'test-app',
|
||||
target: 'build'
|
||||
},
|
||||
azureHosting: {
|
||||
account: "fakeStorageAccount",
|
||||
resourceGroupName: "fake-resource-group",
|
||||
subscription: "fake-subscription-1234",
|
||||
account: 'fakeStorageAccount',
|
||||
resourceGroupName: 'fake-resource-group',
|
||||
subscription: 'fake-subscription-1234'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -68,7 +72,7 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
it('should overwrite existing hosting config', async () => {
|
||||
// Simulate existing app setup
|
||||
let appTree = await initAngularProject();
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise()
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise();
|
||||
appTree.overwrite('/azure.json', appTree.readContent('azure.json').replace(/fake/g, 'existing'));
|
||||
|
||||
const confirmMock = confirm as jest.Mock;
|
||||
|
@ -76,7 +80,7 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
confirmMock.mockImplementationOnce(() => Promise.resolve(true));
|
||||
|
||||
// Run ng add @azure/deploy on existing project
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise()
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise();
|
||||
|
||||
expect(confirm).toHaveBeenCalledTimes(1);
|
||||
expect(appTree.files).toContain('/azure.json');
|
||||
|
@ -86,15 +90,15 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
hosting: [
|
||||
{
|
||||
app: {
|
||||
configuration: "production",
|
||||
path: "dist/test-app",
|
||||
project: "test-app",
|
||||
target: "build",
|
||||
configuration: 'production',
|
||||
path: 'dist/test-app',
|
||||
project: 'test-app',
|
||||
target: 'build'
|
||||
},
|
||||
azureHosting: {
|
||||
account: "fakeStorageAccount",
|
||||
resourceGroupName: "fake-resource-group",
|
||||
subscription: "fake-subscription-1234",
|
||||
account: 'fakeStorageAccount',
|
||||
resourceGroupName: 'fake-resource-group',
|
||||
subscription: 'fake-subscription-1234'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -104,7 +108,7 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
it('should keep existing hosting config', async () => {
|
||||
// Simulate existing app setup
|
||||
let appTree = await initAngularProject();
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise()
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise();
|
||||
appTree.overwrite('/azure.json', appTree.readContent('azure.json').replace(/fake/g, 'existing'));
|
||||
|
||||
const confirmMock = confirm as jest.Mock;
|
||||
|
@ -112,7 +116,7 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
confirmMock.mockImplementationOnce(() => Promise.resolve(false));
|
||||
|
||||
// Run ng add @azure/deploy on existing project
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise()
|
||||
appTree = await testRunner.runSchematicAsync('ng-add', {}, appTree).toPromise();
|
||||
|
||||
expect(confirm).toHaveBeenCalledTimes(1);
|
||||
expect(appTree.files).toContain('/azure.json');
|
||||
|
@ -122,15 +126,15 @@ describe('ng add @azure/ng-deploy', () => {
|
|||
hosting: [
|
||||
{
|
||||
app: {
|
||||
configuration: "production",
|
||||
path: "dist/test-app",
|
||||
project: "test-app",
|
||||
target: "build",
|
||||
configuration: 'production',
|
||||
path: 'dist/test-app',
|
||||
project: 'test-app',
|
||||
target: 'build'
|
||||
},
|
||||
azureHosting: {
|
||||
account: "existingStorageAccount",
|
||||
resourceGroupName: "existing-resource-group",
|
||||
subscription: "existing-subscription-1234",
|
||||
account: 'existingStorageAccount',
|
||||
resourceGroupName: 'existing-resource-group',
|
||||
subscription: 'existing-subscription-1234'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -15,9 +15,7 @@ import { AddOptions } from '../util/shared/types';
|
|||
|
||||
export function ngAdd(_options: AddOptions): Rule {
|
||||
return (tree: Tree, _context: SchematicContext) => {
|
||||
return chain([
|
||||
addDeployAzure(_options)
|
||||
])(tree, _context);
|
||||
return chain([addDeployAzure(_options)])(tree, _context);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -27,8 +25,7 @@ export function addDeployAzure(_options: AddOptions): Rule {
|
|||
const azureJson = readAzureJson(tree);
|
||||
const hostingConfig = azureJson ? getAzureHostingConfig(azureJson, project.projectName) : null;
|
||||
|
||||
if (!hostingConfig || await confirm(`Overwrite existing Azure config for ${ project.projectName }?`)) {
|
||||
|
||||
if (!hostingConfig || (await confirm(`Overwrite existing Azure config for ${project.projectName}?`))) {
|
||||
const auth = await loginToAzure(_context.logger);
|
||||
const credentials = auth.credentials as DeviceTokenCredentials;
|
||||
const subscription = await selectSubscription(auth.subscriptions, _options, _context.logger);
|
||||
|
@ -51,7 +48,6 @@ export function addDeployAzure(_options: AddOptions): Rule {
|
|||
|
||||
// TODO: log url for account at Azure portal
|
||||
generateAzureJson(tree, appDeployConfig, azureDeployConfig);
|
||||
|
||||
}
|
||||
|
||||
project.addLogoutArchitect();
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const loginToAzure = () => Promise.resolve({
|
||||
export const loginToAzure = () =>
|
||||
Promise.resolve({
|
||||
credentials: null,
|
||||
subscriptions: []
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export async function getResourceGroups() {
|
||||
return Promise.resolve([{
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: '1',
|
||||
name: 'mock',
|
||||
location: 'location'
|
||||
|
@ -18,9 +19,8 @@ export async function getResourceGroups() {
|
|||
id: '3',
|
||||
name: 'mock3',
|
||||
location: 'location'
|
||||
}]);
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
export const createResourceGroup = jest.fn((name: string) => Promise.resolve({ name }))
|
||||
|
||||
|
||||
export const createResourceGroup = jest.fn((name: string) => Promise.resolve({ name }));
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const getResourceGroup = () => Promise.resolve({
|
||||
export const getResourceGroup = () =>
|
||||
Promise.resolve({
|
||||
id: '4321',
|
||||
name: 'fake-resource-group',
|
||||
location: 'westus'
|
||||
|
|
|
@ -3,5 +3,4 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const selectSubscription = () => Promise.resolve('fake-subscription-1234');
|
||||
|
||||
export const selectSubscription = () => Promise.resolve('fake-subscription-1234');
|
||||
|
|
|
@ -40,8 +40,8 @@ export async function getAccount(
|
|||
client: StorageManagementClient,
|
||||
resourceGroup: ResourceGroup,
|
||||
options: AddOptions,
|
||||
logger: Logger) {
|
||||
|
||||
logger: Logger
|
||||
) {
|
||||
let accountName = options.account || '';
|
||||
let needToCreateAccount = false;
|
||||
|
||||
|
@ -52,7 +52,7 @@ export async function getAccount(
|
|||
function getInitialAccountName() {
|
||||
const normalizedProjectNameArray = options.project.match(/[a-zA-Z0-9]/g);
|
||||
const normalizedProjectName = normalizedProjectNameArray ? normalizedProjectNameArray.join('') : '';
|
||||
return `${ normalizedProjectName }static`;
|
||||
return `${normalizedProjectName}static`;
|
||||
}
|
||||
|
||||
const initialName = getInitialAccountName();
|
||||
|
@ -63,27 +63,29 @@ export async function getAccount(
|
|||
newAccountPromptOptions.defaultGenerator = generateDefaultAccountName;
|
||||
newAccountPromptOptions.validate = validateAccountName;
|
||||
|
||||
|
||||
if (accountName) {
|
||||
const account = accounts.find(acc => acc.name === accountName);
|
||||
if (!!account) { // account exists
|
||||
if (!!account) {
|
||||
// account exists
|
||||
// TODO: check account configuration
|
||||
logger.info(`Using existing account ${ accountName }`);
|
||||
|
||||
} else { // create account with this name, if valid
|
||||
logger.info(`Using existing account ${accountName}`);
|
||||
} else {
|
||||
// create account with this name, if valid
|
||||
const valid = await validateAccountName(accountName);
|
||||
if (!valid) {
|
||||
accountName = (await newItemPrompt(newAccountPromptOptions)).newAccount;
|
||||
}
|
||||
needToCreateAccount = true;
|
||||
}
|
||||
} else { // no account flag
|
||||
} else {
|
||||
// no account flag
|
||||
|
||||
if (!options.manual) { // quickstart - create w/ default name
|
||||
if (!options.manual) {
|
||||
// quickstart - create w/ default name
|
||||
accountName = await generateDefaultAccountName(initialName);
|
||||
needToCreateAccount = true;
|
||||
|
||||
} else { // select from list or create new
|
||||
} else {
|
||||
// select from list or create new
|
||||
const result = await filteredList(accounts as AccountDetails[], accountPromptOptions, newAccountPromptOptions);
|
||||
needToCreateAccount = !!result.newAccount;
|
||||
accountName = result.newAccount || result.account.name;
|
||||
|
@ -91,7 +93,7 @@ export async function getAccount(
|
|||
}
|
||||
|
||||
if (needToCreateAccount) {
|
||||
spinner.start(`creating ${ accountName }`);
|
||||
spinner.start(`creating ${accountName}`);
|
||||
await createAccount(accountName, client, resourceGroup.name, resourceGroup.location);
|
||||
spinner.succeed();
|
||||
}
|
||||
|
@ -128,18 +130,9 @@ export async function setStaticSiteToPublic(serviceURL: ServiceURL) {
|
|||
});
|
||||
}
|
||||
|
||||
export async function getAccountKey(
|
||||
account: any,
|
||||
client: StorageManagementClient,
|
||||
resourceGroup: any
|
||||
) {
|
||||
const accountKeysRes = await client.storageAccounts.listKeys(
|
||||
resourceGroup,
|
||||
account
|
||||
);
|
||||
const accountKey = (accountKeysRes.keys || []).filter(
|
||||
key => (key.permissions || '').toUpperCase() === 'FULL'
|
||||
)[0];
|
||||
export async function getAccountKey(account: any, client: StorageManagementClient, resourceGroup: any) {
|
||||
const accountKeysRes = await client.storageAccounts.listKeys(resourceGroup, account);
|
||||
const accountKey = (accountKeysRes.keys || []).filter(key => (key.permissions || '').toUpperCase() === 'FULL')[0];
|
||||
if (!accountKey || !accountKey.value) {
|
||||
process.exit(1);
|
||||
return '';
|
||||
|
@ -153,15 +146,11 @@ export async function createAccount(
|
|||
resourceGroupName: string,
|
||||
location: string
|
||||
) {
|
||||
const poller = await client.storageAccounts.beginCreate(
|
||||
resourceGroupName,
|
||||
account,
|
||||
{
|
||||
const poller = await client.storageAccounts.beginCreate(resourceGroupName, account, {
|
||||
kind: 'StorageV2',
|
||||
location,
|
||||
sku: { name: 'Standard_LRS' }
|
||||
}
|
||||
);
|
||||
});
|
||||
await poller.pollUntilFinished();
|
||||
|
||||
spinner.start('Retrieving account keys');
|
||||
|
@ -174,23 +163,14 @@ export async function createAccount(
|
|||
spinner.start('Creating web container');
|
||||
await createWebContainer(client, resourceGroupName, account);
|
||||
spinner.succeed();
|
||||
const pipeline = ServiceURL.newPipeline(
|
||||
new SharedKeyCredential(account, accountKey)
|
||||
);
|
||||
const serviceURL = new ServiceURL(
|
||||
`https://${ account }.blob.core.windows.net`,
|
||||
pipeline
|
||||
);
|
||||
const pipeline = ServiceURL.newPipeline(new SharedKeyCredential(account, accountKey));
|
||||
const serviceURL = new ServiceURL(`https://${account}.blob.core.windows.net`, pipeline);
|
||||
spinner.start('Setting container to be publicly available static site');
|
||||
await setStaticSiteToPublic(serviceURL);
|
||||
spinner.succeed();
|
||||
}
|
||||
|
||||
export async function createWebContainer(
|
||||
client: StorageManagementClient,
|
||||
resourceGroup: any,
|
||||
account: any
|
||||
) {
|
||||
export async function createWebContainer(client: StorageManagementClient, resourceGroup: any, account: any) {
|
||||
await client.blobContainers.create(resourceGroup, account, '$web', {
|
||||
publicAccess: 'Container',
|
||||
metadata: {
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import {
|
||||
interactiveLoginWithAuthResponse,
|
||||
DeviceTokenCredentials,
|
||||
AuthResponse
|
||||
} from '@azure/ms-rest-nodeauth';
|
||||
import { interactiveLoginWithAuthResponse, DeviceTokenCredentials, AuthResponse } from '@azure/ms-rest-nodeauth';
|
||||
import { MemoryCache } from 'adal-node';
|
||||
import { Environment } from '@azure/ms-rest-azure-env';
|
||||
import Conf from 'conf';
|
||||
|
@ -26,14 +22,12 @@ export async function clearCreds() {
|
|||
}
|
||||
|
||||
export async function loginToAzure(logger: Logger): Promise<AuthResponse> {
|
||||
let auth = await globalConfig.get(AUTH) as AuthResponse | null;
|
||||
let auth = (await globalConfig.get(AUTH)) as AuthResponse | null;
|
||||
|
||||
if (auth && auth.credentials) {
|
||||
|
||||
const creds = auth.credentials as DeviceTokenCredentials;
|
||||
const cache = new MemoryCache();
|
||||
cache.add(creds.tokenCache._entries, () => {
|
||||
});
|
||||
cache.add(creds.tokenCache._entries, () => {});
|
||||
|
||||
auth.credentials = new DeviceTokenCredentials(
|
||||
creds.clientId,
|
||||
|
@ -41,7 +35,8 @@ export async function loginToAzure(logger: Logger): Promise<AuthResponse> {
|
|||
creds.username,
|
||||
creds.tokenAudience,
|
||||
new Environment(creds.environment),
|
||||
cache);
|
||||
cache
|
||||
);
|
||||
|
||||
const token = await auth.credentials.getToken();
|
||||
if (new Date(token.expiresOn).getTime() < Date.now()) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { getLocation } from '../locations';
|
||||
import { getLocation } from './locations';
|
||||
|
||||
describe('location', () => {
|
||||
test('should return undefined when locationName is undefined', () => {
|
||||
|
@ -15,4 +15,4 @@ describe('location', () => {
|
|||
expect(actual && actual.id).toBe('southafricanorth');
|
||||
expect(actual && actual.name).toBe('South Africa North');
|
||||
});
|
||||
})
|
||||
});
|
|
@ -8,122 +8,122 @@ export interface StorageLocation {
|
|||
}
|
||||
|
||||
export const defaultLocation = {
|
||||
'id': 'westus',
|
||||
'name': 'West US'
|
||||
id: 'westus',
|
||||
name: 'West US'
|
||||
};
|
||||
|
||||
export const locations = [
|
||||
{
|
||||
'id': 'eastasia',
|
||||
'name': 'East Asia'
|
||||
id: 'eastasia',
|
||||
name: 'East Asia'
|
||||
},
|
||||
{
|
||||
'id': 'southeastasia',
|
||||
'name': 'Southeast Asia'
|
||||
id: 'southeastasia',
|
||||
name: 'Southeast Asia'
|
||||
},
|
||||
{
|
||||
'id': 'centralus',
|
||||
'name': 'Central US'
|
||||
id: 'centralus',
|
||||
name: 'Central US'
|
||||
},
|
||||
{
|
||||
'id': 'eastus',
|
||||
'name': 'East US'
|
||||
id: 'eastus',
|
||||
name: 'East US'
|
||||
},
|
||||
{
|
||||
'id': 'eastus2',
|
||||
'name': 'East US 2'
|
||||
id: 'eastus2',
|
||||
name: 'East US 2'
|
||||
},
|
||||
{
|
||||
'id': 'westus',
|
||||
'name': 'West US'
|
||||
id: 'westus',
|
||||
name: 'West US'
|
||||
},
|
||||
{
|
||||
'id': 'northcentralus',
|
||||
'name': 'North Central US'
|
||||
id: 'northcentralus',
|
||||
name: 'North Central US'
|
||||
},
|
||||
{
|
||||
'id': 'southcentralus',
|
||||
'name': 'South Central US'
|
||||
id: 'southcentralus',
|
||||
name: 'South Central US'
|
||||
},
|
||||
{
|
||||
'id': 'northeurope',
|
||||
'name': 'North Europe'
|
||||
id: 'northeurope',
|
||||
name: 'North Europe'
|
||||
},
|
||||
{
|
||||
'id': 'westeurope',
|
||||
'name': 'West Europe'
|
||||
id: 'westeurope',
|
||||
name: 'West Europe'
|
||||
},
|
||||
{
|
||||
'id': 'japanwest',
|
||||
'name': 'Japan West'
|
||||
id: 'japanwest',
|
||||
name: 'Japan West'
|
||||
},
|
||||
{
|
||||
'id': 'japaneast',
|
||||
'name': 'Japan East'
|
||||
id: 'japaneast',
|
||||
name: 'Japan East'
|
||||
},
|
||||
{
|
||||
'id': 'brazilsouth',
|
||||
'name': 'Brazil South'
|
||||
id: 'brazilsouth',
|
||||
name: 'Brazil South'
|
||||
},
|
||||
{
|
||||
'id': 'australiaeast',
|
||||
'name': 'Australia East'
|
||||
id: 'australiaeast',
|
||||
name: 'Australia East'
|
||||
},
|
||||
{
|
||||
'id': 'australiasoutheast',
|
||||
'name': 'Australia Southeast'
|
||||
id: 'australiasoutheast',
|
||||
name: 'Australia Southeast'
|
||||
},
|
||||
{
|
||||
'id': 'southindia',
|
||||
'name': 'South India'
|
||||
id: 'southindia',
|
||||
name: 'South India'
|
||||
},
|
||||
{
|
||||
'id': 'centralindia',
|
||||
'name': 'Central India'
|
||||
id: 'centralindia',
|
||||
name: 'Central India'
|
||||
},
|
||||
{
|
||||
'id': 'westindia',
|
||||
'name': 'West India'
|
||||
id: 'westindia',
|
||||
name: 'West India'
|
||||
},
|
||||
{
|
||||
'id': 'canadacentral',
|
||||
'name': 'Canada Central'
|
||||
id: 'canadacentral',
|
||||
name: 'Canada Central'
|
||||
},
|
||||
{
|
||||
'id': 'canadaeast',
|
||||
'name': 'Canada East'
|
||||
id: 'canadaeast',
|
||||
name: 'Canada East'
|
||||
},
|
||||
{
|
||||
'id': 'uksouth',
|
||||
'name': 'UK South'
|
||||
id: 'uksouth',
|
||||
name: 'UK South'
|
||||
},
|
||||
{
|
||||
'id': 'ukwest',
|
||||
'name': 'UK West'
|
||||
id: 'ukwest',
|
||||
name: 'UK West'
|
||||
},
|
||||
{
|
||||
'id': 'westcentralus',
|
||||
'name': 'West Central US'
|
||||
id: 'westcentralus',
|
||||
name: 'West Central US'
|
||||
},
|
||||
{
|
||||
'id': 'westus2',
|
||||
'name': 'West US 2'
|
||||
id: 'westus2',
|
||||
name: 'West US 2'
|
||||
},
|
||||
{
|
||||
'id': 'koreacentral',
|
||||
'name': 'Korea Central'
|
||||
id: 'koreacentral',
|
||||
name: 'Korea Central'
|
||||
},
|
||||
{
|
||||
'id': 'koreasouth',
|
||||
'name': 'Korea South'
|
||||
id: 'koreasouth',
|
||||
name: 'Korea South'
|
||||
},
|
||||
{
|
||||
'id': 'francecentral',
|
||||
'name': 'France Central'
|
||||
id: 'francecentral',
|
||||
name: 'France Central'
|
||||
},
|
||||
{
|
||||
'id': 'southafricanorth',
|
||||
'name': 'South Africa North'
|
||||
id: 'southafricanorth',
|
||||
name: 'South Africa North'
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ResourceManagementClient } from "@azure/arm-resources";
|
||||
import { ListItem } from "../prompt/list";
|
||||
import { DeviceTokenCredentials } from "@azure/ms-rest-nodeauth";
|
||||
import { ResourceGroupsCreateOrUpdateResponse } from "@azure/arm-resources/esm/models";
|
||||
import { ResourceManagementClient } from '@azure/arm-resources';
|
||||
import { ListItem } from '../prompt/list';
|
||||
import { DeviceTokenCredentials } from '@azure/ms-rest-nodeauth';
|
||||
import { ResourceGroupsCreateOrUpdateResponse } from '@azure/arm-resources/esm/models';
|
||||
|
||||
export interface ResourceGroupDetails extends ListItem {
|
||||
id: string;
|
||||
|
@ -16,7 +16,7 @@ export interface ResourceGroupDetails extends ListItem {
|
|||
|
||||
export async function getResourceGroups(creds: DeviceTokenCredentials, subscription: string) {
|
||||
const client = new ResourceManagementClient(creds, subscription);
|
||||
const resourceGroupList = await client.resourceGroups.list() as ResourceGroupDetails[];
|
||||
const resourceGroupList = (await client.resourceGroups.list()) as ResourceGroupDetails[];
|
||||
return resourceGroupList;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,8 @@ export async function createResourceGroup(
|
|||
): Promise<ResourceGroupsCreateOrUpdateResponse> {
|
||||
// TODO: throws an error here if the subscription is wrong
|
||||
const client = new ResourceManagementClient(creds, subscription);
|
||||
const resourceGroupRes = await client.resourceGroups.createOrUpdate(name, { location });
|
||||
const resourceGroupRes = await client.resourceGroups.createOrUpdate(name, {
|
||||
location
|
||||
});
|
||||
return resourceGroupRes;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { getResourceGroup, ResourceGroup } from '../resource-group';
|
||||
import { getResourceGroup, ResourceGroup } from './resource-group';
|
||||
import { DeviceTokenCredentials } from '@azure/ms-rest-nodeauth';
|
||||
import { AddOptions } from '../../shared/types';
|
||||
|
||||
import { AddOptions } from '../shared/types';
|
||||
|
||||
const RESOURCE_GROUP = 'GROUP';
|
||||
|
||||
|
@ -21,11 +20,11 @@ const logger = {
|
|||
fatal: jest.fn()
|
||||
};
|
||||
|
||||
jest.mock('../resource-group-helper');
|
||||
jest.mock('../../prompt/name-generator');
|
||||
jest.mock('../../prompt/spinner');
|
||||
jest.mock('./resource-group-helper');
|
||||
jest.mock('../prompt/name-generator');
|
||||
jest.mock('../prompt/spinner');
|
||||
|
||||
import { createResourceGroup } from '../resource-group-helper';
|
||||
import { createResourceGroup } from './resource-group-helper';
|
||||
const createResourceGroupMock: jest.Mock<any, any> = <jest.Mock<any, any>>createResourceGroup;
|
||||
|
||||
describe('resource group', () => {
|
||||
|
@ -34,21 +33,23 @@ describe('resource group', () => {
|
|||
createResourceGroupMock.mockClear();
|
||||
});
|
||||
|
||||
test.only('should create resource group', async() => {
|
||||
test.only('should create resource group', async () => {
|
||||
const subscription = '';
|
||||
await getResourceGroup(credentials, subscription, options, logger);
|
||||
|
||||
expect(createResourceGroupMock.mock.calls[0][0]).toBe(RESOURCE_GROUP);
|
||||
});
|
||||
|
||||
test('should use existing resource group and return it', async() => {
|
||||
test('should use existing resource group and return it', async () => {
|
||||
// there needs to be a match towards resource group list
|
||||
const subscription = '';
|
||||
const existingMockResourceGroup = 'mock2'
|
||||
const optionsWithMatch = { ...options, resourceGroup: existingMockResourceGroup };
|
||||
const existingMockResourceGroup = 'mock2';
|
||||
const optionsWithMatch = {
|
||||
...options,
|
||||
resourceGroup: existingMockResourceGroup
|
||||
};
|
||||
const resourceGroup: ResourceGroup = await getResourceGroup(credentials, subscription, optionsWithMatch, logger);
|
||||
|
||||
|
||||
expect(createResourceGroupMock.mock.calls.length).toBe(0);
|
||||
|
||||
expect(logger.info.mock.calls.length).toBe(1);
|
|
@ -39,9 +39,11 @@ const locationPromptOptions = {
|
|||
};
|
||||
|
||||
export async function getResourceGroup(
|
||||
creds: DeviceTokenCredentials, subscription: string, options: AddOptions, logger: Logger
|
||||
creds: DeviceTokenCredentials,
|
||||
subscription: string,
|
||||
options: AddOptions,
|
||||
logger: Logger
|
||||
): Promise<ResourceGroup> {
|
||||
|
||||
let resourceGroupName = options.resourceGroup || '';
|
||||
let location = getLocation(options.location);
|
||||
|
||||
|
@ -53,26 +55,26 @@ export async function getResourceGroup(
|
|||
const initialName = options.project + '-static-deploy';
|
||||
const defaultResourceGroupName = await resourceGroupNameGenerator(initialName, resourceGroupList);
|
||||
|
||||
if (!options.manual) { // quickstart
|
||||
if (!options.manual) {
|
||||
// quickstart
|
||||
resourceGroupName = resourceGroupName || defaultResourceGroupName;
|
||||
location = location || defaultLocation;
|
||||
}
|
||||
|
||||
if (!!resourceGroupName) { // provided or quickstart + default
|
||||
if (!!resourceGroupName) {
|
||||
// provided or quickstart + default
|
||||
result = resourceGroupList.find(rg => rg.name === resourceGroupName);
|
||||
if (!!result) {
|
||||
logger.info(`Using existing resource group ${ resourceGroupName }`);
|
||||
logger.info(`Using existing resource group ${resourceGroupName}`);
|
||||
}
|
||||
} else { // not provided + manual
|
||||
} else {
|
||||
// not provided + manual
|
||||
|
||||
// TODO: default name can be assigned later, only if creating a new resource group.
|
||||
// TODO: check availability of the default name
|
||||
newResourceGroupsPromptOptions.default = defaultResourceGroupName;
|
||||
|
||||
result = (await filteredList(
|
||||
resourceGroupList,
|
||||
resourceGroupsPromptOptions,
|
||||
newResourceGroupsPromptOptions));
|
||||
result = await filteredList(resourceGroupList, resourceGroupsPromptOptions, newResourceGroupsPromptOptions);
|
||||
|
||||
// TODO: add check whether the new resource group doesn't already exist.
|
||||
// Currently throws an error of exists in a different location:
|
||||
|
@ -83,8 +85,8 @@ export async function getResourceGroup(
|
|||
}
|
||||
|
||||
if (!result || result.newResourceGroup) {
|
||||
location = location || await askLocation(); // if quickstart - location defined above
|
||||
spinner.start(`Creating resource group ${ resourceGroupName } at ${ location.name } (${ location.id })`);
|
||||
location = location || (await askLocation()); // if quickstart - location defined above
|
||||
spinner.start(`Creating resource group ${resourceGroupName} at ${location.name} (${location.id})`);
|
||||
result = await createResourceGroup(resourceGroupName, subscription, creds, location.id);
|
||||
spinner.succeed();
|
||||
}
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { selectSubscription } from '../subscription';
|
||||
import { selectSubscription } from './subscription';
|
||||
import { LinkedSubscription } from '@azure/ms-rest-nodeauth';
|
||||
import { AddOptions } from '../../shared/types';
|
||||
import { AddOptions } from '../shared/types';
|
||||
|
||||
jest.mock('inquirer');
|
||||
|
||||
// AddOptions, Logger
|
||||
|
||||
|
||||
const SUBID = '124';
|
||||
const SUBNAME = 'name';
|
||||
|
||||
|
@ -38,26 +37,28 @@ describe('subscription', () => {
|
|||
});
|
||||
|
||||
test('should throw error when input is an EMPTY array', async () => {
|
||||
const errorMessage = 'You don\'t have any active subscriptions. ' +
|
||||
const errorMessage =
|
||||
"You don't have any active subscriptions. " +
|
||||
'Head to https://azure.com/free and sign in. From there you can create a new subscription ' +
|
||||
'and then you can come back and try again.';
|
||||
|
||||
|
||||
expect(selectSubscription([], optionsMock, loggerMock)).rejects.toEqual(new Error(errorMessage))
|
||||
expect(selectSubscription([], optionsMock, loggerMock)).rejects.toEqual(new Error(errorMessage));
|
||||
});
|
||||
|
||||
test('provided sub id DOES NOT match when provided in options', async() => {
|
||||
const subs = <Array<LinkedSubscription>>[{
|
||||
test('provided sub id DOES NOT match when provided in options', async () => {
|
||||
const subs = <Array<LinkedSubscription>>[
|
||||
{
|
||||
id: '456',
|
||||
name: 'a sub'
|
||||
}];
|
||||
}
|
||||
];
|
||||
|
||||
selectSubscription(subs, optionsMock, loggerMock);
|
||||
|
||||
const warnCalledTwice = loggerMock.warn.mock.calls.length === 2;
|
||||
|
||||
expect(loggerMock.warn.mock.calls[0][0]).toBe(`The provided subscription ID does not exist.`);
|
||||
expect(loggerMock.warn.mock.calls[1][0]).toBe(`Using subscription ${subs[0].name} - ${subs[0].id}`)
|
||||
expect(loggerMock.warn.mock.calls[1][0]).toBe(`Using subscription ${subs[0].name} - ${subs[0].id}`);
|
||||
expect(warnCalledTwice).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -73,21 +74,18 @@ describe('subscription', () => {
|
|||
});
|
||||
|
||||
test('should throw error when input is undefined', async () => {
|
||||
const errorMessage = 'API returned no subscription IDs. It should. ' +
|
||||
'Log in to https://portal.azure.com and see if there\'s something wrong with your account.';
|
||||
|
||||
const errorMessage =
|
||||
'API returned no subscription IDs. It should. ' +
|
||||
"Log in to https://portal.azure.com and see if there's something wrong with your account.";
|
||||
|
||||
// this one looks a bit weird because method is `async`, otherwise throwError() helper should be used
|
||||
expect(selectSubscription(undefined, optionsMock, loggerMock)).rejects.toEqual(new Error(errorMessage))
|
||||
expect(selectSubscription(undefined, optionsMock, loggerMock)).rejects.toEqual(new Error(errorMessage));
|
||||
});
|
||||
|
||||
test('should prompt user to select a subscription if more than one subscription', async () => {
|
||||
const expected = 'subMock'; // check inquirer.js at __mocks__ at root level
|
||||
|
||||
const subs = <Array<LinkedSubscription>>[
|
||||
{ id: 'abc', name: 'subMock' },
|
||||
{ id: '123', name: 'sub2' }
|
||||
];
|
||||
const subs = <Array<LinkedSubscription>>[{ id: 'abc', name: 'subMock' }, { id: '123', name: 'sub2' }];
|
||||
const actual = await selectSubscription(subs, optionsMock, loggerMock);
|
||||
|
||||
// TODO verify that prompt is being invoked
|
||||
|
@ -95,4 +93,3 @@ describe('subscription', () => {
|
|||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
|
@ -6,7 +6,6 @@ import { LinkedSubscription } from '@azure/ms-rest-nodeauth';
|
|||
import { prompt } from 'inquirer';
|
||||
import { AddOptions, Logger } from '../shared/types';
|
||||
|
||||
|
||||
export async function selectSubscription(
|
||||
subs: LinkedSubscription[] | undefined,
|
||||
options: AddOptions,
|
||||
|
@ -15,7 +14,7 @@ export async function selectSubscription(
|
|||
if (Array.isArray(subs)) {
|
||||
if (subs.length === 0) {
|
||||
throw new Error(
|
||||
'You don\'t have any active subscriptions. ' +
|
||||
"You don't have any active subscriptions. " +
|
||||
'Head to https://azure.com/free and sign in. From there you can create a new subscription ' +
|
||||
'and then you can come back and try again.'
|
||||
);
|
||||
|
@ -35,7 +34,7 @@ export async function selectSubscription(
|
|||
|
||||
if (subs.length === 1) {
|
||||
if (subProvided) {
|
||||
logger.warn(`Using subscription ${ subs[0].name } - ${ subs[0].id }`);
|
||||
logger.warn(`Using subscription ${subs[0].name} - ${subs[0].id}`);
|
||||
}
|
||||
return subs[0].id;
|
||||
} else {
|
||||
|
@ -44,7 +43,7 @@ export async function selectSubscription(
|
|||
type: 'list',
|
||||
name: 'sub',
|
||||
choices: subs.map(choice => ({
|
||||
name: `${ choice.name } – ${ choice.id }`,
|
||||
name: `${choice.name} – ${choice.id}`,
|
||||
value: choice.id
|
||||
})),
|
||||
message: 'Under which subscription should we put this static site?'
|
||||
|
@ -56,6 +55,6 @@ export async function selectSubscription(
|
|||
|
||||
throw new Error(
|
||||
'API returned no subscription IDs. It should. ' +
|
||||
'Log in to https://portal.azure.com and see if there\'s something wrong with your account.'
|
||||
"Log in to https://portal.azure.com and see if there's something wrong with your account."
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
console.log('using mock spinner');
|
||||
export const spinner = {
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
succeed: jest.fn()
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ export interface PromptOptions {
|
|||
name?: string;
|
||||
message: string;
|
||||
default?: string;
|
||||
defaultGenerator?: ((name: string) => Promise<string>);
|
||||
defaultGenerator?: (name: string) => Promise<string>;
|
||||
title?: string;
|
||||
validate?: any;
|
||||
id: string;
|
||||
|
@ -38,11 +38,11 @@ export async function filteredList(list: ListItem[], listOptions: PromptOptions,
|
|||
}
|
||||
|
||||
export async function newItemPrompt(newItemOptions: PromptOptions) {
|
||||
let item, valid = true;
|
||||
const defaultValue =
|
||||
newItemOptions.defaultGenerator ?
|
||||
await newItemOptions.defaultGenerator(newItemOptions.default || '') :
|
||||
newItemOptions.default;
|
||||
let item,
|
||||
valid = true;
|
||||
const defaultValue = newItemOptions.defaultGenerator
|
||||
? await newItemOptions.defaultGenerator(newItemOptions.default || '')
|
||||
: newItemOptions.default;
|
||||
do {
|
||||
item = await (inquirer as any).prompt({
|
||||
type: 'input',
|
||||
|
|
|
@ -7,7 +7,7 @@ export async function generateName(name: string, validate: (name: string) => Pro
|
|||
do {
|
||||
valid = await validate(name);
|
||||
if (!valid) {
|
||||
name = `${ name }${ Math.ceil(Math.random() * 100) }`;
|
||||
name = `${name}${Math.ceil(Math.random() * 100)}`;
|
||||
}
|
||||
} while (!valid);
|
||||
return name;
|
||||
|
|
|
@ -8,21 +8,15 @@ const ora = require('ora');
|
|||
export const spinner = ora({
|
||||
text: 'Rounding up all the reptiles',
|
||||
spinner: {
|
||||
frames: [
|
||||
chalk.red('▌'),
|
||||
chalk.green('▀'),
|
||||
chalk.yellow('▐'),
|
||||
chalk.blue('▄')
|
||||
],
|
||||
frames: [chalk.red('▌'), chalk.green('▀'), chalk.yellow('▐'), chalk.blue('▄')],
|
||||
interval: 100
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export function spin (msg?: string) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
export function spin(msg?: string) {
|
||||
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
descriptor.value = async function () {
|
||||
descriptor.value = async function() {
|
||||
spinner.start(msg);
|
||||
let result;
|
||||
try {
|
||||
|
@ -35,6 +29,4 @@ export function spin (msg?: string) {
|
|||
};
|
||||
return descriptor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@ export class AngularWorkspace {
|
|||
this.project = this.getProject(options);
|
||||
this.target = 'build'; // TODO allow configuration of other options
|
||||
this.configuration = 'production';
|
||||
this.path = this.project.architect ? this.project.architect[this.target].options.outputPath : `dist/${ this.projectName }`;
|
||||
this.path = this.project.architect
|
||||
? this.project.architect[this.target].options.outputPath
|
||||
: `dist/${this.projectName}`;
|
||||
}
|
||||
|
||||
getPath() {
|
||||
|
@ -47,10 +49,7 @@ export class AngularWorkspace {
|
|||
getWorkspace() {
|
||||
let schema: experimental.workspace.WorkspaceSchema;
|
||||
try {
|
||||
schema = parseJson(
|
||||
this.content,
|
||||
JsonParseMode.Loose
|
||||
) as {} as experimental.workspace.WorkspaceSchema;
|
||||
schema = (parseJson(this.content, JsonParseMode.Loose) as {}) as experimental.workspace.WorkspaceSchema;
|
||||
} catch (e) {
|
||||
throw new SchematicsException(`Could not parse angular.json: ` + e.message);
|
||||
}
|
||||
|
@ -73,7 +72,6 @@ export class AngularWorkspace {
|
|||
}
|
||||
|
||||
getProject(options: any) {
|
||||
|
||||
const project = this.schema.projects[this.projectName];
|
||||
if (!project) {
|
||||
throw new SchematicsException('Project is not defined in this workspace');
|
||||
|
@ -83,12 +81,15 @@ export class AngularWorkspace {
|
|||
throw new SchematicsException(`Deploy requires a project type of "application" in angular.json`);
|
||||
}
|
||||
|
||||
if (!project.architect ||
|
||||
if (
|
||||
!project.architect ||
|
||||
!project.architect.build ||
|
||||
!project.architect.build.options ||
|
||||
!project.architect.build.options.outputPath) {
|
||||
!project.architect.build.options.outputPath
|
||||
) {
|
||||
throw new SchematicsException(
|
||||
`Cannot read the output path (architect.build.options.outputPath) of project "${ this.projectName }" in angular.json`);
|
||||
`Cannot read the output path (architect.build.options.outputPath) of project "${this.projectName}" in angular.json`
|
||||
);
|
||||
}
|
||||
|
||||
return project;
|
||||
|
@ -126,5 +127,4 @@ export class AngularWorkspace {
|
|||
|
||||
this.updateTree();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ function safeReadJSON(path: string, tree: Tree) {
|
|||
}
|
||||
return JSON.parse(json.toString());
|
||||
} catch (e) {
|
||||
throw new SchematicsException(`Error when parsing ${ path }: ${ e.message }`);
|
||||
throw new SchematicsException(`Error when parsing ${path}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "tsconfig",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"es2015",
|
||||
"dom"
|
||||
],
|
||||
"lib": ["es2018", "dom"],
|
||||
"outDir": "out",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
|
@ -24,17 +20,8 @@
|
|||
"sourceMap": true,
|
||||
"strictNullChecks": true,
|
||||
"target": "es6",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"src/*/files/**/*",
|
||||
"**/__mocks__/*",
|
||||
"**/__tests__/*"
|
||||
]
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["**/*.spec.ts", "src/*/files/**/*", "**/__mocks__/*", "**/__tests__/*"]
|
||||
}
|
||||
|
|
69
tslint.json
69
tslint.json
|
@ -1,69 +0,0 @@
|
|||
{
|
||||
"rulesDirectory": ["codelyzer"],
|
||||
"rules": {
|
||||
"arrow-return-shorthand": true,
|
||||
"callable-types": true,
|
||||
"class-name": true,
|
||||
"comment-format": [true, "check-space"],
|
||||
"curly": true,
|
||||
"deprecation": {
|
||||
"severity": "warn"
|
||||
},
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"import-blacklist": [true, "rxjs/Rx"],
|
||||
"import-spacing": true,
|
||||
"indent": [true, "spaces"],
|
||||
"interface-over-type-literal": true,
|
||||
"label-position": true,
|
||||
"max-classes-per-file": [true, 1],
|
||||
"max-line-length": [true, 140],
|
||||
"member-access": false,
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": ["static-field", "instance-field", "static-method", "instance-method"]
|
||||
}
|
||||
],
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-super": true,
|
||||
"no-empty": false,
|
||||
"no-empty-interface": true,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": [true, "ignore-params"],
|
||||
"no-misused-new": true,
|
||||
"no-non-null-assertion": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-string-literal": false,
|
||||
"no-string-throw": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unnecessary-initializer": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"],
|
||||
"prefer-const": true,
|
||||
"quotemark": [true, "single"],
|
||||
"radix": true,
|
||||
"semicolon": [true, "always"],
|
||||
"triple-equals": [true, "allow-null-check"],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"unified-signatures": true,
|
||||
"variable-name": false,
|
||||
"whitespace": [true, "check-branch", "check-decl", "check-operator", "check-separator", "check-type"]
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче