Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relative rootdir #620

Merged
merged 6 commits into from
May 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ If `newValue` is omitted it returns the current setting value.
Like `.gitignore`, `.claspignore` allows you to ignore files that you do not wish to not upload on `clasp push`. Steps:

1. Create a file called `.claspignore` in your project's root directory.
2. Add patterns to be excluded from `clasp push`. _Note_: The `.claspignore` file is parsed with [Anymatch](https://github.com/micromatch/anymatch), which is different from `.gitignore`, especially for directories. To ignore a directory, use syntax like `**/node_modules/**`.
1. Add patterns to be excluded from `clasp push`. _Note_: The `.claspignore` patterns are applied by [multimatch](https://github.com/sindresorhus/multimatch), which is different from `.gitignore`, especially for directories. To ignore a directory, use syntax like `**/node_modules/**`.

A sample `.claspignore` ignoring everything except the manifest and `build/main.js`:

Expand All @@ -450,6 +450,8 @@ A sample `.claspignore` ignoring everything except the manifest and `build/main.
!appsscript.json
```

_Note_: The `.claspignore` patterns are applied relative from the `rootDir`.

## Project Settings File (`.clasp.json`)

When running `clone` or `create`, a file named `.clasp.json` is created in the current directory to describe `clasp`'s configuration for the current project. Example `.clasp.json`:
Expand Down
47 changes: 29 additions & 18 deletions src/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import * as recursive from 'recursive-readdir';
import * as ts from 'typescript';
import { loadAPICredentials, script } from './auth';
import { DOT, DOTFILE } from './dotfile';
import { ERROR, LOG, checkIfOnline, getAPIFileType, getProjectSettings, logError, spinner } from './utils';
import {
ERROR, LOG, PROJECT_MANIFEST_FILENAME,
checkIfOnline, getAPIFileType, getProjectSettings, logError, spinner,
} from './utils';

import * as ts2gas from 'ts2gas';
import * as findParentDir from 'find-parent-dir';
Expand Down Expand Up @@ -79,29 +82,33 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
// Note: filePaths contain relative paths such as "test/bar.ts", "../../src/foo.js"
recursive(rootDir, async (err, filePaths) => {
if (err) return callback(err, null, null);

// Filter files that aren't allowed.
const ignorePatterns = await DOTFILE.IGNORE();
filePaths.sort(); // Sort files alphanumerically
let abortPush = false;
const nonIgnoredFilePaths: string[] = [];
const file2path: Array<{ path: string; file: AppsScriptFile }> = []; // used by `filePushOrder`
let ignoredFilePaths: string[] = [];
ignoredFilePaths = ignoredFilePaths.concat(ignorePatterns);

// Replace OS specific path separator to common '/' char for console output
filePaths = filePaths.map((name) => name.replace(/\\/g, '/'));
filePaths.sort(); // Sort files alphanumerically

// check ignore files
const ignoreMatches = multimatch(filePaths, ignorePatterns, { dot: true });
const intersection: string[] = filePaths.filter(file => !ignoreMatches.includes(file));
// dispatch with patterns from .claspignore
const filesToPush: string[] = [];
const filesToIgnore: string[] = [];
filePaths.forEach(file => {
if (multimatch(path.relative(rootDir, file), ignorePatterns, { dot: true }).length === 0) {
filesToPush.push(file);
} else {
filesToIgnore.push(file);
}
});

// Check if there are files that will conflict if renamed .gs to .js.
// When pushing to Apps Script, these files will overwrite each other.
intersection.forEach((name: string) => {
let abortPush = false;
filesToPush.forEach((name: string) => {
const fileNameWithoutExt = name.slice(0, -path.extname(name).length);
if (
intersection.indexOf(fileNameWithoutExt + '.js') !== -1 &&
intersection.indexOf(fileNameWithoutExt + '.gs') !== -1
filesToPush.indexOf(fileNameWithoutExt + '.js') !== -1 &&
filesToPush.indexOf(fileNameWithoutExt + '.gs') !== -1
) {
// Can't rename, conflicting files
abortPush = true;
Expand All @@ -113,8 +120,12 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
});
if (abortPush) return callback(new Error(), null, null);

const nonIgnoredFilePaths: string[] = [];
const ignoredFilePaths = [...filesToIgnore];

const file2path: Array<{ path: string; file: AppsScriptFile }> = []; // used by `filePushOrder`
// Loop through files that are not ignored
let files = intersection
let files = filesToPush
.map((name, i) => {
const normalizedName = path.normalize(name);

Expand All @@ -135,7 +146,7 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
const formattedName = getAppsScriptFileName(rootDir, name);

// If the file is valid, return the file in a format suited for the Apps Script API.
if (isValidFileName(name, type, rootDir, normalizedName, ignoreMatches)) {
if (isValidFileName(name, type, rootDir, normalizedName, filesToIgnore)) {
nonIgnoredFilePaths.push(name);
const file: AppsScriptFile = {
name: formattedName, // the file base name
Expand All @@ -155,7 +166,7 @@ export async function getProjectFiles(rootDir: string = path.join('.', '/'), cal
// It puts the files in the setting's filePushOrder first.
// This is needed because Apps Script blindly executes files in order of creation time.
// The Apps Script API updates the creation time of files.
if (filePushOrder) {
if (filePushOrder && filePushOrder.length > 0) { // skip "filePushOrder": []
spinner.stop(true);
console.log('Detected filePushOrder setting. Pushing these files first:');
filePushOrder.forEach(file => {
Expand Down Expand Up @@ -202,9 +213,9 @@ export function isValidFileName(name: string,
let isValidJSONIfJSON = true;
if (type === 'JSON') {
if (rootDir) {
isValidJSONIfJSON = normalizedName === path.join(rootDir, 'appsscript.json');
isValidJSONIfJSON = normalizedName === path.join(rootDir, PROJECT_MANIFEST_FILENAME);
} else {
isValidJSONIfJSON = name === 'appsscript.json';
isValidJSONIfJSON = name === PROJECT_MANIFEST_FILENAME;
}
} else {
// Must be SERVER_JS or HTML.
Expand Down
26 changes: 21 additions & 5 deletions tests/commands/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('Test clasp status function', () => {
it('should respect globs and negation rules when rootDir given', () => {
const tmpdir = setupTmpDirectory([
{ file: '.clasp.json', data: '{ "scriptId":"1234", "rootDir":"dist" }' },
{ file: '.claspignore', data: '**/**\n!dist/build/main.js\n!dist/appsscript.json' },
{ file: '.claspignore', data: '**/**\n!build/main.js\n!appsscript.json' },
{ file: 'dist/build/main.js', data: TEST_CODE_JS },
{ file: 'dist/appsscript.json', data: TEST_APPSSCRIPT_JSON_WITHOUT_RUN_API },
{ file: 'dist/shouldBeIgnored', data: TEST_CODE_JS },
Expand All @@ -75,11 +75,27 @@ describe('Test clasp status function', () => {
expect(result.status).to.equal(0);
const resultJson = JSON.parse(result.stdout);
expect(resultJson.untrackedFiles).to.have.members([
'**/**',
'!dist/build/main.js',
'!dist/appsscript.json']);
'dist/should/alsoBeIgnored',
'dist/shouldBeIgnored']);
expect(resultJson.filesToPush).to.have.members(['dist/build/main.js', 'dist/appsscript.json']);
// TODO test with a rootDir with a relative directory like "../src"
});
it('should respect globs and negation rules when relative rootDir given', () => {
const tmpdir = setupTmpDirectory([
{ file: 'src/.clasp.json', data: '{ "scriptId":"1234", "rootDir":"../build" }' },
{ file: 'src/.claspignore', data: '**/**\n!main.js\n!appsscript.json' },
{ file: 'build/main.js', data: TEST_CODE_JS },
{ file: 'build/appsscript.json', data: TEST_APPSSCRIPT_JSON_WITHOUT_RUN_API },
{ file: 'build/shouldBeIgnored', data: TEST_CODE_JS },
{ file: 'build/should/alsoBeIgnored', data: TEST_CODE_JS },
]);
spawnSync(CLASP, ['create', '[TEST] clasp status'], { encoding: 'utf8', cwd: tmpdir + '/src' });
const result = spawnSync(CLASP, ['status', '--json'], { encoding: 'utf8', cwd: tmpdir + '/src' });
expect(result.status).to.equal(0);
const resultJson = JSON.parse(result.stdout);
expect(resultJson.untrackedFiles).to.have.members([
'../build/should/alsoBeIgnored',
'../build/shouldBeIgnored']);
expect(resultJson.filesToPush).to.have.members(['../build/main.js', '../build/appsscript.json']);
});
after(cleanup);
});