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

Commit

Permalink
add ignore globs to zip functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
goya committed Sep 7, 2018
1 parent d17fc36 commit 4d84043
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 99 deletions.
2 changes: 1 addition & 1 deletion index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pgb-api",
"version": "1.0.5",
"version": "1.1.0",
"description": "nodeJS API to PhoneGap Build",
"keywords": [
"PhoneGap",
Expand Down
8 changes: 6 additions & 2 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const merge = require('./misc').merge
const getPath = require('./misc').getPath
const mkdirp = require('./misc').mkdirp
const rest = require('./rest-client')
const zipper = require('./zipper')
const zip = require('./zip')
const fs = require('fs')
const path = require('path')
const os = require('os')
Expand Down Expand Up @@ -171,9 +171,13 @@ class PGBApi {
addAppFromDir(id, dir, data) {
return new Promise((resolve, reject) => {
let cleanup = false

let filePath = data.zip
delete data.zip

let ignore = data.ignore || []
delete data.ignore

if (!filePath) {
filePath = path.join(os.tmpdir(), 'pgb-' + Math.random().toString(32).slice(2) + '.zip')
cleanup = true
Expand All @@ -199,7 +203,7 @@ class PGBApi {
}

emit('debug', `archiving ${dir} to ${filePath}`)
zipper.zipDir(dir, filePath, this.defaults.events)
zip(dir, filePath, this.defaults.events, ignore)
.then(() => this.addAppFromFile(id, filePath, data))
.then((app) => {
deleteZip()
Expand Down
100 changes: 100 additions & 0 deletions src/glob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const fs = require('fs')
const path = require('path')

const glob = (root, ignoreGlobs) => {
let list = []
let skipped = []
let globRegexes = (ignoreGlobs || []).map(toGlobRegex)

let walkSync = dir => {
let files = fs.readdirSync(dir)

files.forEach(file => {
let fullPath = path.join(dir, file)
let globPath = path.resolve(fullPath).replace(path.resolve(root) + '/', '')

if (file.startsWith('.') && !file.match(/^\.pgb/)) {
skipped.push(`${fullPath} [HIDDEN]`)
return
}

try {
let stat = fs.statSync(fullPath)
fs.closeSync(fs.openSync(fullPath, 'r'))

let ignored = filter(globPath, stat.isDirectory(), globRegexes)

if (stat.isDirectory()) {
if (ignored) {
skipped.push(`${fullPath}/ [IGNORED]`)
} else {
list.push({ path: fullPath, size: 0 })
walkSync(fullPath)
}
} else {
if (ignored) {
skipped.push(`${fullPath} [IGNORED]`)
} else {
list.push({ path: fullPath, size: stat.size })
}
list.push()
}
} catch (e) {
skipped.push(`${fullPath} [${e.code}]`)
}
})
}

walkSync(root)
return { list, skipped }
}

const toGlobRegex = (glob) => {
if (glob == null || glob[0] === '#' || glob.trim() === '') return null

let negation = glob.indexOf('!') === 0
let dir = false

if (glob.endsWith('/')) {
glob = glob.slice(0, -1)
dir = true
}

if (negation || glob[0] === '/') {
glob = glob.slice(1)
}

glob = glob
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
.replace(/\?/g, '.')
.replace(/\*\*\//g, '^e^')
.replace(/\*\*/g, '^e^')
.replace(/\*/g, `[^/]+`)
.replace(/\^e\^/g, '.*')

if (glob.indexOf('/') === -1) {
glob = glob + '/?'
}

return { dir, negation, regex: new RegExp(`^${glob}$`) }
}

const filter = (filePath, isDir, globRegexes) => {
let result = false

for (let globRegex of globRegexes) {
if (globRegex == null) continue

if (globRegex.dir && !isDir) continue

if (globRegex.regex.test(filePath)) {
if (globRegex.dir && isDir) {
return !globRegex.negation
}
result = !globRegex.negation
}
}
return result
}

module.exports = { glob, toGlobRegex, filter }
3 changes: 1 addition & 2 deletions src/rest-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ const urlParse = require('url')
const fs = require('fs')
const path = require('path')
const Stream = require('stream').Stream
const version = require('../package.json').version
const https = require('https')

const defaultOpts = {
headers: {
'User-Agent': `pgb-api/${version} node/${process.version} (${process.platform})`
'User-Agent': `pgb-api/1.1.0 node/${process.version} (${process.platform})`
}
}

Expand Down
45 changes: 6 additions & 39 deletions src/zipper.js → src/zip.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
const fs = require('fs')
const yazl = require('yazl')
const path = require('path')
const glob = require('./glob').glob

const getFileList = (dir) => {
let list = []
let skipped = []

let walkSync = dir => {
let files = fs.readdirSync(dir)

files.forEach(file => {
let fullPath = path.join(dir, file)
if (file.startsWith('.') && !file.match(/^\.pgb/)) {
skipped.push(`${fullPath} [HIDDEN]`)
return
}

try {
let stat = fs.statSync(fullPath)
fs.closeSync(fs.openSync(fullPath, 'r'))

if (stat.isDirectory()) {
list.push({ path: fullPath, size: 0 })
walkSync(fullPath)
} else {
list.push({ path: fullPath, size: stat.size })
}
} catch (e) {
skipped.push(`${fullPath} [${e.code}]`)
}
})
}

walkSync(dir)
return { list, skipped }
}

const zipDir = (dir, dest, eventEmitter) => {
const zipDir = (dir, dest, eventEmitter, ignore) => {
return new Promise((resolve, reject) => {
let fileList = getFileList(dir)
let files = glob(dir, ignore)
let stream = fs.createWriteStream(dest)
let zip = new yazl.ZipFile()
let file = ''
Expand All @@ -51,9 +18,9 @@ const zipDir = (dir, dest, eventEmitter) => {
if (eventEmitter) eventEmitter.emit(evt, data)
}

emit('zip/files', fileList)
emit('zip/files', files)

for (let f of fileList.list) {
for (let f of files.list) {
let pathInArchive = path.relative(dir, f.path)
if (fs.statSync(f.path).isDirectory()) {
zip.addEmptyDirectory(pathInArchive)
Expand Down Expand Up @@ -102,4 +69,4 @@ const zipDir = (dir, dest, eventEmitter) => {
})
}

module.exports = { getFileList, zipDir }
module.exports = zipDir
8 changes: 4 additions & 4 deletions test/api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const restClient = require('../src/rest-client')
const zipper = require('../src/zipper')
const zipper = require('../src/zip')
jest.mock('../src/rest-client')
jest.mock('../src/zipper')
jest.mock('../src/zip')
const apiClient = require('../src/api')
const fs = require('fs')
const os = require('os')
Expand Down Expand Up @@ -261,7 +261,7 @@ describe('api', () => {
os.tmpdir = jest.fn().mockImplementation(() => '/tmp')
fs.mkdirSync('/tmp')
fs.mkdirSync('/app_to_zip')
zipper.zipDir.mockImplementation((src, dest) => {
zipper.mockImplementation((src, dest) => {
fs.writeFileSync(dest)
return Promise.resolve()
})
Expand Down Expand Up @@ -326,7 +326,7 @@ describe('api', () => {
})

test('zip dir and add app with bad files', (done) => {
zipper.zipDir.mockImplementation((src, dest) => Promise.reject(new Error('zip failed')))
zipper.mockImplementation((src, dest) => Promise.reject(new Error('zip failed')))

return api.addApp('/app_to_zip', { hydrates: true }).catch((val) => {
expect(val).toEqual(new Error('zip failed'))
Expand Down
94 changes: 94 additions & 0 deletions test/glob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const glob = require('../src/glob')
const os = require('os')
const fs = require('fs')

beforeEach(() => {
os.tmpdir = jest.fn().mockImplementation(() => '/tmp')
fs.mkdirSync('/tmp')
fs.mkdirSync('/app_to_zip')
fs.writeFileSync('/app_to_zip/.delete_me', '1')
fs.writeFileSync('/app_to_zip/.pgb_dont_delete_me', '22')
fs.writeFileSync('/app_to_zip/index.html', '333')
fs.writeFileSync('/app_to_zip/cordova.js', '4444')
fs.mkdirSync('/app_to_zip/res')
})

afterEach(() => {
fs.rmdirSync('/tmp')
fs.unlinkSync('/app_to_zip/.delete_me')
fs.unlinkSync('/app_to_zip/.pgb_dont_delete_me')
fs.unlinkSync('/app_to_zip/index.html')
fs.unlinkSync('/app_to_zip/cordova.js')
fs.rmdirSync('/app_to_zip/res')
fs.rmdirSync('/app_to_zip')
})

describe('glob', () => {
test('should return files', () => {
let result = {
list: [
{ 'path': '/app_to_zip/.pgb_dont_delete_me', 'size': 2 },
{ 'path': '/app_to_zip/cordova.js', 'size': 4 },
{ 'path': '/app_to_zip/index.html', 'size': 3 },
{ 'path': '/app_to_zip/res', 'size': 0 }
],
skipped: [ '/app_to_zip/.delete_me [HIDDEN]' ]
}
expect(glob.glob('/app_to_zip')).toEqual(result)
})

test('should skip if file cant be read', () => {
let error = new Error('bad file')
error.code = 'ENOENT'
let oldOpenSync = fs.openSync
fs.openSync = jest.fn().mockImplementation(() => { throw error })
let result = {
skipped: [
'/app_to_zip/.delete_me [HIDDEN]',
'/app_to_zip/.pgb_dont_delete_me [ENOENT]',
'/app_to_zip/cordova.js [ENOENT]',
'/app_to_zip/index.html [ENOENT]',
'/app_to_zip/res [ENOENT]'
],
list: [ ]
}
expect(glob.glob('/app_to_zip')).toEqual(result)
fs.openSync = oldOpenSync
})

test('should skip ignored files', () => {
let result = {
list: [
{ 'path': '/app_to_zip/.pgb_dont_delete_me', 'size': 2 },
{ 'path': '/app_to_zip/cordova.js', 'size': 4 }
],
skipped: [ '/app_to_zip/.delete_me [HIDDEN]', '/app_to_zip/index.html [IGNORED]', '/app_to_zip/res/ [IGNORED]' ]
}
expect(glob.glob('/app_to_zip', [ '**/*.html', 'res/' ])).toEqual(result)
})
})

describe('toGlobRegex', () => {
test('should skip ignored files', () => {
expect(glob.toGlobRegex('')).toEqual(null)
expect(glob.toGlobRegex(null)).toEqual(null)
expect(glob.toGlobRegex('# comment')).toEqual(null)
expect(glob.toGlobRegex('shell')).toEqual({ 'dir': false, 'negation': false, 'regex': /^shell\/?$/ })
expect(glob.toGlobRegex('dir/')).toEqual({ 'dir': true, 'negation': false, 'regex': /^dir\/?$/ })
expect(glob.toGlobRegex('dir/**')).toEqual({ 'dir': false, 'negation': false, 'regex': /^dir\/.*$/ })
expect(glob.toGlobRegex('/dir')).toEqual({ 'dir': false, 'negation': false, 'regex': /^dir\/?$/ })
expect(glob.toGlobRegex('!not_me')).toEqual({ 'dir': false, 'negation': true, 'regex': /^not_me\/?$/ })
expect(glob.toGlobRegex('(.moot?[]+)')).toEqual({ 'dir': false, 'negation': false, 'regex': /^\(\.moot.\[\]\+\)\/?$/ })
})
})

describe('filter', () => {
test('should skip ignored files', () => {
expect(glob.filter('', true, [])).toEqual(false)
expect(glob.filter('', true, [null])).toEqual(false)
expect(glob.filter('rabbit', true, [{ dir: true, negation: false, regex: /^rabbit\/?$/ }])).toEqual(true)
expect(glob.filter('rabbit', true, [{ dir: false, negation: false, regex: /^rabbit\/?$/ }])).toEqual(true)
expect(glob.filter('rabbit', true, [{ dir: true, negation: true, regex: /^rabbit$/ }])).toEqual(false)
expect(glob.filter('rabbit', false, [{ dir: true, negation: false, regex: /^rabbit\/?$/ }])).toEqual(false)
})
})
20 changes: 8 additions & 12 deletions test/rest-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fs = require('fs')
const server = require('./_helpers/fake-server')
const app = server.listen(3000, '0.0.0.0')
const reqs = jest.spyOn(server, 'requestLogger')
const version = require('../package.json').version
const lastReq = () => reqs.mock.calls[reqs.mock.calls.length - 1][0]

jest.mock('https', () => {
Expand All @@ -27,18 +28,13 @@ describe('rest-client', () => {
})

describe('#get', () => {
test('use http for http url and https for https url', () => {
let spy1 = jest.spyOn(require('http'), 'request')
let spy2 = jest.spyOn(require('https'), 'request')

return restClient.get('http://localhost:3000/page1')
.then((response) => expect(response).toBe('A page'))
.then(() => expect(spy1).toBeCalled())
.then(() => restClient.get('https://localhost:3000/page1'))
.then((response) => expect(response).toBe('A page'))
.catch(() => { /* it will fail the get */ })
.then(() => expect(spy2).toBeCalled())
})
test('should populate user-agent (remember to manually update the user-agent!!!)', () =>
restClient.get('http://localhost:3000/page1')
.then((response) => {
let userAgent = `pgb-api/${version} node/${process.version} (${process.platform})`
expect(reqs.mock.calls[0][0].headers['user-agent']).toEqual(userAgent)
})
)

test('should get simple html', () =>
restClient.get('http://localhost:3000/page1')
Expand Down
Loading

0 comments on commit 4d84043

Please sign in to comment.