507 строки
14 KiB
CoffeeScript
507 строки
14 KiB
CoffeeScript
through = require 'through2'
|
|
util = require 'util'
|
|
|
|
|
|
# place an object into global namespace
|
|
global['Import'] = (object) ->
|
|
for key, value of object
|
|
global[key] = value
|
|
|
|
Import
|
|
# require into the global namespace (modulename = module)
|
|
Install: (modulename, module) ->
|
|
global[modulename] = require module or modulename
|
|
|
|
# require a gulp-Plugin into the global namespace
|
|
Plugin: () ->
|
|
Install module,"gulp-#{module}" for module in arguments
|
|
|
|
# require a module, placing exports into global namespace
|
|
Include: () ->
|
|
Import require module for module in arguments
|
|
|
|
Tasks: () ->
|
|
require "#{__dirname}/#{module}" for module in arguments
|
|
|
|
###############################################
|
|
# force-global a bunch of stuff.
|
|
require 'shelljs/global'
|
|
Install 'marked'
|
|
Install 'vinyl'
|
|
Install 'os'
|
|
Install 'path'
|
|
Install 'fs'
|
|
Install 'gulp'
|
|
Install 'util'
|
|
Install 'moment'
|
|
Install 'chalk'
|
|
Install 'yargs'
|
|
Install 'semver'
|
|
|
|
Install 'eol', 'gulp-line-ending-corrector'
|
|
Install 'through', 'through2-parallel'
|
|
Install 'run', 'run-sequence'
|
|
|
|
# do a bit of monkeypatching
|
|
_gulpStart = gulp.Gulp::start
|
|
_runTask = gulp.Gulp::_runTask
|
|
|
|
gulp.Gulp::start = (taskName) ->
|
|
@currentStartTaskName = taskName
|
|
_gulpStart.apply this, arguments
|
|
return
|
|
|
|
gulp.Gulp::_runTask = (task) ->
|
|
@currentRunTaskName = task.name
|
|
_runTask.apply this, arguments
|
|
return
|
|
|
|
# echo 'this.currentStartTaskName: ' + this.currentStartTaskName
|
|
# echo 'this.currentRunTaskName: ' + this.currentRunTaskName
|
|
|
|
# bring some gulp-Plugins along
|
|
# Plugin 'filter',
|
|
# 'zip'
|
|
#'unzip'
|
|
#'rename'
|
|
|
|
# force this into global namespace
|
|
global['argv'] = yargs.argv
|
|
|
|
fs = require('fs')
|
|
path = require('path')
|
|
|
|
concurrency = 0
|
|
queue = []
|
|
global.completed = []
|
|
vfs = require('vinyl-fs');
|
|
|
|
module.exports =
|
|
# lets us just handle each item in a stream easily.
|
|
foreach: (delegate) ->
|
|
through.obj { concurrency: threshold }, ( each, enc, done ) ->
|
|
delegate each, done, this
|
|
|
|
count: (result,passthru) =>
|
|
foreach (each,done) =>
|
|
result++
|
|
done null
|
|
|
|
hashCode: (s) ->
|
|
(s.split('').reduce ((a, b) ->
|
|
a = (a << 5) - a + b.charCodeAt(0)
|
|
a & a
|
|
), 0 ) .toString(16)
|
|
|
|
toArray: (result,passthru) =>
|
|
foreach (each,done) =>
|
|
result.push(each)
|
|
if passthru
|
|
done null, each
|
|
else
|
|
done null
|
|
|
|
showFiles: () ->
|
|
foreach (each,done) ->
|
|
echo info each.path
|
|
done null, each
|
|
|
|
onlyFiles: () ->
|
|
foreach (each,done) ->
|
|
return done null, each if fs.statSync(each.path).isFile()
|
|
done null
|
|
|
|
source: (globs, options ) ->
|
|
options = options or { }
|
|
options.follow = true
|
|
vfs.src( globs, options)
|
|
|
|
watchFiles: (src,tasks) ->
|
|
return gulp.watch( src,tasks)
|
|
|
|
destination: (globs, options ) ->
|
|
gulp.dest( globs, options)
|
|
|
|
later: (fn) ->
|
|
setTimeout fn, 10
|
|
|
|
mklink: (link,target) ->
|
|
# unlink link
|
|
if ! test "-d", link
|
|
fs.symlinkSync target, link, "junction"
|
|
|
|
unlink: (link) ->
|
|
if test "-d", link
|
|
fs.unlinkSync link
|
|
|
|
erase: (file) ->
|
|
if test "-f", file
|
|
fs.unlinkSync file
|
|
|
|
task: (name, description, deps, fn) ->
|
|
throw "Invalid task name " if typeof name isnt 'string'
|
|
throw "Invalid task description #{name} " if typeof description isnt 'string'
|
|
|
|
if typeof deps == 'function'
|
|
fn = deps
|
|
deps = []
|
|
|
|
# chain the task if it's a repeat
|
|
if name of gulp.tasks
|
|
prev = gulp.tasks[name]
|
|
|
|
# reset the name of this task to be a 'child'' task
|
|
name = "#{name}/#{description}"
|
|
description = ''
|
|
|
|
# add this task as a dependency of the original task.
|
|
prev.dep.unshift name
|
|
|
|
# add the new task.
|
|
# gulp.task name, deps, fn
|
|
skip = (name.startsWith "init") or (name.startsWith "npm-install") or (name.startsWith "clean") or (name is "copy-dts-files") or (name.startsWith "nuke") or (name.startsWith "reset") or (name.startsWith "autorest") or description.endsWith("!")
|
|
|
|
description = '' if description = '!'
|
|
|
|
if !skip
|
|
deps.unshift "init"
|
|
|
|
if fn.length # see if the task function has arguments (betcha never saw that before!)
|
|
gulp.task name, deps, (done)->
|
|
if not global.completed[name]
|
|
#echo warning "Running task #{name} #{typeof done}"
|
|
global.completed[name] = true
|
|
return fn(done)
|
|
#echo warning "Skipping completed task #{name}"
|
|
return done()
|
|
else
|
|
gulp.task name, deps, ()->
|
|
if not global.completed[name]
|
|
#echo warning "Running task #{name}"
|
|
global.completed[name] = true
|
|
return fn()
|
|
#echo warning "Skipping completed task #{name}"
|
|
return null
|
|
|
|
|
|
# set the description
|
|
gulp.tasks[name].description = description
|
|
|
|
return
|
|
|
|
where: (predicate) ->
|
|
foreach (each,done) ->
|
|
#return done null if each?
|
|
return done null, each if predicate each
|
|
done null
|
|
|
|
splitPath: (path) ->
|
|
s = path.match /^(.+)[\\\/]([^\/]+)$/ or [path, '',path]
|
|
f = s[2].match(/^(.*)([\\.].*)$/ ) or [s[2],s[2],'']
|
|
d = (path.match /^(.:)[\\\/]?(.*)$/ ) or ['','',path]
|
|
return {
|
|
fullname : path
|
|
folder : s[1]
|
|
filename : s[2]
|
|
basename : f[1]
|
|
extension : f[2]
|
|
drive: d[1] or ''
|
|
folders: (d[2].split /[\\\/]/ )or path
|
|
}
|
|
|
|
folder: (path) ->
|
|
return '' if not path
|
|
return (splitPath path).folder
|
|
|
|
split: (path) ->
|
|
return '' if not path
|
|
return (splitPath path).folders
|
|
|
|
filename: (path) ->
|
|
return '' if not path
|
|
p = splitPath path
|
|
return p.filename
|
|
|
|
extension: (path) ->
|
|
return '' if not path
|
|
p = splitPath path
|
|
return p.extension
|
|
|
|
basename: (path) ->
|
|
return '' if not path
|
|
p = splitPath path
|
|
return p.basename
|
|
|
|
exists: (path) ->
|
|
return test '-f', path
|
|
|
|
fileExists: (path) ->
|
|
return test '-f', path
|
|
|
|
dirExists: (path) ->
|
|
return test '-d', path
|
|
|
|
newer: (first,second) ->
|
|
return true if (!test "-d", second) and (!test "-f", second)
|
|
return false if (!test "-d",first) and (!test "-f", first)
|
|
f = fs.statSync(first).mtime
|
|
s = fs.statSync(second).mtime
|
|
return f > s
|
|
|
|
flattenEncode: (path) ->
|
|
path.basename = "#{ path.dirname.replace(/[\/\\]/g, '_') }_#{path.basename}"
|
|
path.dirname = ""
|
|
|
|
flattenDecode: (path) ->
|
|
f = path.basename.match(/^(.*)_(.*)$/ )
|
|
path.basename = "#{f[1].replace(/[_]/g, '/') }/#{f[2]}"
|
|
path.dirname = ""
|
|
|
|
except: (match) ->
|
|
# await through.obj defer file, enc, callback
|
|
through.obj (file, enc, callback) ->
|
|
|
|
# check if the file is an actual file.
|
|
# if it's not, just skip this tool.
|
|
if !file or !file.path
|
|
return callback null, file
|
|
|
|
# do something with the file
|
|
if file.path.match( match )
|
|
return callback null
|
|
|
|
return callback null, file
|
|
|
|
|
|
rmfile: (dir, file, callback) ->
|
|
p = path.join(dir, file)
|
|
fs.lstat p, (err, stat) ->
|
|
if err
|
|
callback.call null, err
|
|
else if stat.isDirectory()
|
|
rmdir p, callback
|
|
else
|
|
fs.unlink p, callback
|
|
return
|
|
return
|
|
|
|
rmdir: (dir, callback) ->
|
|
#echo "RMDIR #{dir}"
|
|
fs.readdir dir, (err, files) ->
|
|
if err
|
|
callback.call null, err
|
|
else if files.length
|
|
i = undefined
|
|
j = undefined
|
|
i = j = files.length
|
|
while i--
|
|
rmfile dir, files[i], (err) ->
|
|
if err
|
|
callback.call null, err
|
|
else if --j == 0
|
|
fs.rmdir dir, callback
|
|
return
|
|
else
|
|
fs.rmdir dir, callback
|
|
return
|
|
return
|
|
|
|
guid: ->
|
|
x = -> Math.floor((1 + Math.random()) * 0x10000).toString(16).substring 1
|
|
"#{x()}#{x()}-#{x()}-#{x()}-#{x()}-#{x()}#{x()}#{x()}"
|
|
|
|
Fail: (text) ->
|
|
echo ""
|
|
echo "#{ error 'Task Failed:' } #{error_message text}"
|
|
echo ""
|
|
rm '-rf', workdir
|
|
process.exit(1)
|
|
|
|
execute: (cmdline,options,callback, ondata)->
|
|
if typeof options == 'function'
|
|
ondata = callback
|
|
callback = options
|
|
options = { }
|
|
|
|
# if we're busy, schedule again...
|
|
if concurrency >= threshold
|
|
queue.push(->
|
|
execute cmdline, options, callback, ondata
|
|
)
|
|
return
|
|
|
|
concurrency++
|
|
|
|
options.cwd = options.cwd or basefolder
|
|
echo " #{quiet_info options.cwd} :: #{info cmdline}" if !options.silent
|
|
|
|
options.silent = !verbose
|
|
|
|
proc = exec cmdline, options, (code,stdout,stderr)->
|
|
concurrency--
|
|
|
|
if code and (options.retry or 0) > 0
|
|
echo warning "retrying #{options.retry} #{options.cwd}/#{cmdline}"
|
|
options.retry--
|
|
return execute cmdline,options,callback,ondata
|
|
|
|
|
|
# run the next one in the queue
|
|
if queue.length
|
|
fn = (queue.shift())
|
|
fn()
|
|
|
|
if code and !options.ignoreexitcode
|
|
echo error "Exec Failed #{quiet_info options.cwd} :: #{info cmdline}"
|
|
if( stderr.length )
|
|
echo error "(stderr)"
|
|
echo marked ">> #{error stderr}"
|
|
if( stdout.length )
|
|
echo warning "(stdout)"
|
|
echo warning stdout
|
|
|
|
Fail "Execute Task failed, fast exit"
|
|
callback(code,stdout,stderr)
|
|
|
|
proc.stdout.on 'data', ondata if ondata
|
|
return proc
|
|
|
|
autorest: (args,done,ignoreexitcode) ->
|
|
echo info "Queuing up: AutoRest #{args.join(' ')}"
|
|
execute "#{basefolder}/node_modules/.bin/autorest \"--use=#{basefolder}\" #{args.map((a) -> "\"#{a}\"").join(' ')}" , { silent:true, ignoreexitcode: ignoreexitcode || false }, (code,stdout,stderr) ->
|
|
return done(code,stdout,stderr)
|
|
|
|
# build task for global build
|
|
module.exports.task 'build', 'builds project', ->
|
|
echo "Building project in #{basefolder}"
|
|
|
|
module.exports.task 'clean', 'cleans the project files', ->
|
|
|
|
module.exports.task 'regenerate', 'regenerates expected files for testing', ->
|
|
|
|
|
|
# task for vs code
|
|
module.exports.task 'code', 'launches vs code', ->
|
|
exec "code #{basefolder}"
|
|
|
|
module.exports.task 'release-only', '', (done)->
|
|
Fail( "This command requires --configuration release" ) if configuration isnt "Release"
|
|
done()
|
|
|
|
configString = (s)->
|
|
"#{s.charAt 0 .toUpperCase()}#{s.slice 1 .toLowerCase() }"
|
|
|
|
# bring current module into global namespace.
|
|
Import module.exports
|
|
|
|
###############################################
|
|
# Global values
|
|
process.env.tmp = process.env.tmp or "#{basefolder}/tmp"
|
|
|
|
package_json = require("#{basefolder}/package.json")
|
|
|
|
|
|
Import
|
|
stable: argv.stable or false
|
|
configuration: if argv.configuration then configString( argv.configuration) else (if argv.release then 'Release' else 'Debug')
|
|
github_apikey: argv.github_apikey or process.env.GITHUB_APIKEY or null
|
|
nuget_apikey: argv.nuget_apikey or process.env.NUGET_APIKEY or null
|
|
npm_apikey: argv.npm_apikey or process.env.NPM_APIKEY or null
|
|
today: moment().format('YYYYMMDD')
|
|
now: moment().format('YYYYMMDD-HHmm')
|
|
force: argv.force or false
|
|
threshold: argv.threshold or ((os.cpus().length)-1) or 1
|
|
verbose: argv.verbose or null
|
|
workdir: "#{process.env.tmp}/gulp/#{module.exports.guid()}"
|
|
watch: argv.watch or false
|
|
|
|
mkdir "-p", workdir if !test "-d", workdir
|
|
|
|
###############################################
|
|
# UI stuff
|
|
TerminalRenderer = require('marked-terminal')
|
|
marked.setOptions {
|
|
renderer: new TerminalRenderer({
|
|
heading: chalk.green.bold,
|
|
firstHeading: chalk.green.bold,
|
|
showSectionPrefix: false,
|
|
strong: chalk.bold.cyan,
|
|
em: chalk.cyan,
|
|
blockquote: chalk.magenta,
|
|
tab: 2
|
|
})
|
|
}
|
|
|
|
set '+e'
|
|
|
|
Import
|
|
error: chalk.bold.red
|
|
error_message: chalk.bold.cyan
|
|
warning: chalk.bold.yellow
|
|
info: chalk.bold.green
|
|
quiet_info: chalk.green
|
|
|
|
###############################################
|
|
task 'default','', ->
|
|
cmds = ""
|
|
|
|
for name, t of gulp.tasks
|
|
cmds += "\n gulp **#{name}** - #{t.description}" if t.description? and t.description.length
|
|
switches = ""
|
|
|
|
echo marked """
|
|
|
|
# Usage
|
|
|
|
## gulp commands
|
|
#{cmds}
|
|
|
|
## available switches
|
|
*--force* specify when you want to force an action (restore, etc)
|
|
*--configuration* 'debug' or 'release'
|
|
*--verbose* enable verbose output
|
|
*--threshold=nn* set parallelism threshold (default = 10)
|
|
|
|
#{switches}
|
|
"""
|
|
|
|
task 'test', "Run Tests", ->
|
|
|
|
task 'fix-line-endings', 'Fixes line endings to file-type appropriate values.', ->
|
|
source "**/*.iced"
|
|
.pipe eol {eolc: 'LF', encoding:'utf8'}
|
|
.pipe destination '.'
|
|
|
|
task 'get-tag', '!', (done)->
|
|
if argv.tag
|
|
# take the argument if they specified it.
|
|
global.tag = argv.tag
|
|
done()
|
|
else
|
|
# pick up the tag from the pkg.json version entry
|
|
global.tag = semver.parse((package_json.version).trim()).prerelease.join(".")
|
|
if( global.tag )
|
|
return done()
|
|
|
|
# if branch is provided by environment
|
|
if( env.BUILD_SOURCEBRANCHNAME )
|
|
global.tag = if ( env.BUILD_SOURCEBRANCHNAME == "master" || env.BUILD_SOURCEBRANCHNAME =="HEAD" ) then "preview" else env.BUILD_SOURCEBRANCHNAME
|
|
return done();
|
|
|
|
# grab the git branch name.
|
|
execute "git rev-parse --abbrev-ref HEAD" , {silent:true}, (c,o,e)->
|
|
o = "preview" if( o == undefined || o == null || o == "" || o.trim() == 'master' || o.trim() == 'HEAD')
|
|
global.tag = o.trim()
|
|
done();
|
|
|
|
task 'version-number', '!', (done)->
|
|
if argv.version
|
|
global.version = argv.version if argv.version
|
|
done();
|
|
else
|
|
# git rev-list --parents HEAD --count --full-history
|
|
execute "git rev-list --parents HEAD --count --full-history" , {silent:true}, (c,o,e)->
|
|
pv = (package_json.version).trim()
|
|
global.version = "#{semver.major(pv)}.#{semver.minor(pv)}.#{o.trim()}"
|
|
done();
|