diff --git a/.gitignore b/.gitignore index 65074064..92b3b4b0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ node_modules/ .npm .env .nyc_output -*.lock \ No newline at end of file +*.lock +/tmp diff --git a/index.ts b/index.ts index 7d9a8bb8..67d7aea2 100755 --- a/index.ts +++ b/index.ts @@ -442,23 +442,25 @@ commander commander .command('status') .description('Lists files that will be pushed by clasp') -.action(async () => { +.option('--json', "Show status in JSON form") +.action(async (cmd: { json: boolean }) => { await checkIfOnline(); getProjectSettings().then(({ scriptId, rootDir }: ProjectSettings) => { if (!scriptId) return; getProjectFiles(rootDir, (err, projectFiles) => { if(err) return console.log(err); else if (projectFiles) { - const [nonIgnoredFilePaths, ignoredFilePaths] = projectFiles; - console.log(LOG.STATUS_PUSH); - nonIgnoredFilePaths.map((filePath: string) => { - console.log(`└─ ${filePath}`); - }); - if (ignoredFilePaths.length) { + const status = { + filesToPush: projectFiles[0], + untrackedFiles: projectFiles[1] + } + if (cmd.json) { + console.log(JSON.stringify(status)); + } else { + console.log(LOG.STATUS_PUSH); + status.filesToPush.forEach((file) => console.log(`└─ ${file}`)); console.log(LOG.STATUS_IGNORE); - ignoredFilePaths.map((filePath: string) => { - console.log(`└─ ${filePath}`); - }); + status.untrackedFiles.forEach((file) => console.log(`└─ ${file}`)); } } }); diff --git a/package-lock.json b/package-lock.json index 48a0b3a8..75de1378 100644 --- a/package-lock.json +++ b/package-lock.json @@ -181,6 +181,12 @@ "integrity": "sha512-7WcbyctkE8GTzogDb0ulRAEw7v8oIS54ft9mQTU7PfM0hp5e+8kpa+HeQ7IQrFbKtJXBKcZ4bh+Em9dTw5L6AQ==", "dev": true }, + "@types/chai": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", + "integrity": "sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==", + "dev": true + }, "@types/cli-spinner": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@types/cli-spinner/-/cli-spinner-0.2.0.tgz", @@ -231,6 +237,15 @@ "integrity": "sha512-y3bR98mzYOo0pAZuiLari+cQyiKk3UXRuT45h1RjhfeCzqkjaVsfZJNaxdgtk7/3tzOm1ozLTqEqMP3VbI48jw==", "dev": true }, + "@types/fs-extra": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.2.tgz", + "integrity": "sha512-Q3FWsbdmkQd1ib11A4XNWQvRD//5KpPoGawA8aB2DR7pWKoW9XQv3+dGxD/Z1eVFze23Okdo27ZQytVFlweKvQ==", + "dev": true, + "requires": { + "@types/node": "9.4.0" + } + }, "@types/glob": { "version": "5.0.35", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.35.tgz", @@ -262,6 +277,12 @@ "@types/node": "9.4.0" } }, + "@types/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-YeDiSEzznwZwwp766SJ6QlrTyBYUGPSIwmREHVTmktUYiT/WADdWtpt9iH0KuUSf8lZLdI4lP0X6PBzPo5//JQ==", + "dev": true + }, "@types/node": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.0.tgz", @@ -289,6 +310,12 @@ "@types/node": "9.4.0" } }, + "@types/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=", + "dev": true + }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -1782,6 +1809,28 @@ "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=" }, + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "4.0.0", + "universalify": "0.1.1" + }, + "dependencies": { + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + } + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7961,6 +8010,12 @@ "object-keys": "1.0.11" } }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 8dbdf01c..508c823f 100644 --- a/package.json +++ b/package.json @@ -57,21 +57,27 @@ }, "devDependencies": { "@types/anymatch": "^1.3.0", + "@types/chai": "^4.1.3", "@types/cli-spinner": "^0.2.0", "@types/commander": "^2.12.2", "@types/connect": "^3.4.31", "@types/del": "^3.0.0", "@types/events": "^1.1.0", + "@types/fs-extra": "^5.0.2", "@types/glob": "^5.0.35", "@types/mkdirp": "^0.5.2", + "@types/mocha": "^5.2.0", "@types/node": "^9.4.0", "@types/open": "^0.0.29", "@types/pluralize": "^0.0.28", "@types/recursive-readdir": "^2.2.0", + "@types/tmp": "0.0.33", "chai": "^4.1.2", "coveralls": "^3.0.0", + "fs-extra": "^6.0.1", "mocha": "^5.1.0", "nyc": "^11.6.0", + "tmp": "0.0.33", "tslint": "^5.9.1", "typescript": "^2.8.1" } diff --git a/tests/test.ts b/tests/test.ts index 48955320..cb7f955c 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -1,12 +1,13 @@ import { describe, it } from 'mocha'; import { expect } from 'chai'; -import * as fs from 'fs'; +import * as fs from 'fs-extra'; import * as os from 'os'; const { spawnSync } = require('child_process'); import { getScriptURL, getFileType, getAPIFileType, saveProjectId } from './../src/utils.js'; -const path = require('path'); +import * as path from 'path'; +import * as tmp from 'tmp'; describe('Test help for each function', () => { it('should output help for run command', () => { @@ -18,7 +19,7 @@ describe('Test help for each function', () => { }); it('should output help for logs command', () => { const result = spawnSync( - 'clasp', ['logs', '--help'], { encoding : 'utf8', detached: true }, + 'clasp', ['logs', '--help'], { encoding : 'utf8' }, ); expect(result.status).to.equal(0); expect(result.stdout).to.include('Shows the StackDriver logs'); @@ -106,6 +107,46 @@ describe.skip('Test clasp push function', () => { }); }); +describe.skip('Test clasp status function', () => { + function setupTmpDirectory(filepathsAndContents: Array<{ file: string, data: string }>) { + fs.ensureDirSync('tmp'); + const tmpdir = tmp.dirSync({ unsafeCleanup: true, dir: 'tmp/', keep: true }).name; + filepathsAndContents.forEach(({ file, data }) => { + fs.outputFileSync(path.join(tmpdir, file), data); + }); + return tmpdir; + } + it("should respect globs and negation rules", () => { + const tmpdir = setupTmpDirectory([ + { file: '.claspignore', data: '**/**\n!build/main.js\n!appsscript.json' }, + { file: 'build/main.js', data: ' ' }, + { file: 'appsscript.json', data: ' ' }, + { file: 'shouldBeIgnored', data: ' ' }, + { file: 'should/alsoBeIgnored', data: ' ' } + ]); + spawnSync('clasp', ['create', '[TEST] clasp status'], { encoding: 'utf8', cwd: tmpdir }); + const result = spawnSync('clasp', ['status', '--json'], { encoding: 'utf8', cwd: tmpdir }); + expect(result.status).to.equal(0); + const resultJson = JSON.parse(result.stdout); + expect(resultJson.untrackedFiles).to.have.members(['shouldBeIgnored', 'should/alsoBeIgnored']); + expect(resultJson.filesToPush).to.have.members(['build/main.js', 'appsscript.json']); + }); + // https://github.com/google/clasp/issues/67 - This test currently fails + it.skip('should ignore dotfiles if the parent folder is ignored', () => { + const tmpdir = setupTmpDirectory([ + { file: '.claspignore', data: '**/node_modules/**\n**/**\n!appsscript.json' }, + { file: 'appsscript.json', data: ' ' }, + { file: 'node_modules/fsevents/build/Release/.deps/Release/.node.d', data: ' ' }, + ]); + spawnSync('clasp', ['create', '[TEST] clasp status'], { encoding: 'utf8', cwd: tmpdir }); + const result = spawnSync('clasp', ['status', '--json'], { encoding: 'utf8', cwd: tmpdir }); + expect(result.status).to.equal(0); + const resultJson = JSON.parse(result.stdout); + expect(resultJson.untrackedFiles).to.have.members(['node_modules/fsevents/build/Release/.deps/Release/.node.d']); + expect(resultJson.filesToPush).to.have.members(['appsscript.json']); + }); +}); + describe.skip('Test clasp open function', () => { it('should open a project correctly', () => { const result = spawnSync(