-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a1f80ca
Showing
10 changed files
with
308 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[*.coffee] | ||
tab_width = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# deno build | ||
bin | ||
tmp | ||
|
||
# bootstrap cache | ||
tools/tmp* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"deno.enable":true,"deno.lint":true,"deno.unstable":true} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
COPYRIGHT 2022 HEIN THANT MAUNG MAUNG | ||
|
||
REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET: | ||
|
||
1. REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE, THIS LIST OF CONDITIONS AND THE FOLLOWING DISCLAIMER. | ||
|
||
2. REDISTRIBUTIONS IN BINARY FORM MUST REPRODUCE THE ABOVE COPYRIGHT NOTICE, THIS LIST OF CONDITIONS AND THE FOLLOWING DISCLAIMER IN THE DOCUMENTATION AND/OR OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# XueRun ( 雪 run ) | ||
|
||
Just a make-like task runner but with more power! It's written in CoffeeScript + Deno. | ||
|
||
## How to Build | ||
|
||
First, clone this repo. Then, run "tools/bootstrap.ts" | ||
|
||
```shell | ||
deno run tools/bootstrap.ts | ||
``` | ||
|
||
## License | ||
|
||
XueRun is licensed under BSD-2-Clause. For more, see [LICENSE](LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import * as eta from "https://deno.land/x/[email protected]/mod.ts" | ||
|
||
eta.configure | ||
tags: ["{{", "}}"] | ||
useWith: true | ||
parse: { exec: "#", raw: "%", interpolate: "" } | ||
|
||
# magic happens here | ||
export runRecipe = (rc, recipe, options, recon, asIngredient) -> | ||
if not (rc.hasOwnProperty(recipe)) | ||
console.error("\nxuerun: oops, recipe '#{recipe}' is not in .xuerun tasks!\n") | ||
Deno.exit(1) | ||
# resolve dependencies | ||
currentRecipe = rc[recipe] | ||
|
||
currentOption = {} | ||
if typeof currentRecipe.passEnv == "boolean" and currentRecipe.passEnv | ||
currentOption = { ...currentOption, ...Deno.env.toObject() } | ||
else if Array.isArray(currentRecipe.passEnv) | ||
currentOption = { ...currentOption } | ||
currentRecipe.passEnv.forEach (env) -> currentOption[env] = Deno.env.get(env) | ||
else ### do pass env ### | ||
|
||
if asIngredient then currentOption = { ...currentOption, ...options } | ||
else if typeof currentRecipe.passCLI == "boolean" and currentRecipe.passCLI | ||
currentOption = { ...currentOption, ...options } | ||
else if Array.isArray(currentRecipe.passCLI) | ||
# since current task is main task ( not ingredient ), options point to CLI option | ||
currentOption = { ...currentOption } | ||
currentRecipe.passCLI.forEach (opt) -> currentOption[opt] = options[opt] || "" | ||
else ### don't pass options ### | ||
|
||
dependencies = if typeof currentRecipe.dependencies == | ||
"string" then currentRecipe.dependencies.split(" ") else currentRecipe.dependencies | ||
|
||
dependencies.forEach (dep) -> | ||
# won't pass options | ||
if typeof dep == "string" then return runRecipe(rc, dep, {}) | ||
|
||
depOption = { ...dep.options } | ||
if typeof dep.passParentOptions == "boolean" and dep.passParentOptions | ||
depOption = { ...currentOption, ...depOption } | ||
else if Array.isArray(dep.passParentOptions) | ||
dep.passParentOptions.forEach (opt) -> depOption[opt] = currentOption[opt] | ||
else ### do pass parent option ### | ||
|
||
# resolve ( compile ) option value with current option | ||
depOptionToBePassed = {} | ||
Object.entries(depOption).forEach ([option, value]) -> | ||
unless typeof value == "string" then return depOptionToBePassed[option] = value | ||
try depOptionToBePassed[option] = eta.render(value, currentOption) | ||
catch err | ||
console.error("\nxuerun: oops, something went wrong while reading options.\nError:", | ||
err.message, "\n") | ||
Deno.exit(1) | ||
runRecipe(rc, dep.name, depOptionToBePassed, recon, true) | ||
|
||
# make main recipe | ||
commands = currentRecipe.command | ||
(if typeof commands == "string" then [commands] else commands) | ||
.map (cmdOption) -> | ||
try | ||
if typeof cmdOption == "string" then return eta.render(cmdOption, currentOption) | ||
else return {...cmdOption, cmd: eta.render(cmdOption.cmd, currentOption)} | ||
catch err | ||
console.error( | ||
"\nxuerun: oops, something went wrong while reading command.\nError:", | ||
err.message, "\n") | ||
Deno.exit(1) | ||
.forEach (cmdOption) -> | ||
commandToRun = [ | ||
(if typeof cmdOption == "object" and | ||
cmdOption.shell then cmdOption.shell else currentRecipe.shell), "-c", | ||
if typeof cmdOption == "string" then cmdOption else cmdOption.cmd ] | ||
|
||
# if recon, just show command | ||
if recon then return console.info(commandToRun) | ||
|
||
# run command | ||
preparedEnv = {} | ||
Object.entries(currentOption).forEach ([k, v]) -> | ||
preparedEnv[k] = switch | ||
when v == null then "" | ||
when typeof v == "undefined" then "" | ||
else v.toString() | ||
|
||
commandProcess = null | ||
try | ||
commandProcess = Deno.run | ||
cmd: commandToRun | ||
stdin: "inherit" | ||
stdout: "inherit" | ||
stderr: "inherit" | ||
clearEnv: true | ||
env: preparedEnv | ||
catch err | ||
console.error("\nxuerun: Something went wrong while running command", commandToRun) | ||
console.error("Error:", err.message, "\n") | ||
|
||
if commandProcess == null then Deno.exit(1) | ||
status = await commandProcess.status() | ||
if status.code != 0 | ||
console.error("\nxuerun: command exit with exit code:", status.code, "\n") | ||
Deno.exit(status.code) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { create, defaulted, optional, union, object, record, | ||
array, string, number, boolean, validate } from "https://esm.sh/superstruct"; | ||
|
||
XueRunUserCmd = union([string(), object({ shell: optional(string()), cmd: string() })]); | ||
|
||
XueRunIngredient$option = union([string(), number(), boolean()]) | ||
export XueRunIngredient = object | ||
name: string(), | ||
options: defaulted(optional(record(string(), XueRunIngredient$option)), () => ({})), | ||
passParentOptions: defaulted(optional(union([boolean(), array(string())])), () => !1), | ||
|
||
getCurrentSH = -> if Deno.build.os == "windows" then "cmd" else "sh" | ||
|
||
XueRunRecipe$dependencies = union([string(), array(union([string(), XueRunIngredient]))]) | ||
export XueRunRecipe = object | ||
description: defaulted(optional(string()), () => ""), | ||
shell: defaulted(optional(string()), () => Deno.env.get("SHELL") || getCurrentSH()), | ||
command: defaulted(optional(union([string(), array(XueRunUserCmd)])), () => ""), | ||
passEnv: defaulted(optional(union([boolean(), array(string())])), () => !1), | ||
passCLI: defaulted(optional(union([boolean(), array(string())])), () => !1), | ||
dependencies: defaulted(optional(XueRunRecipe$dependencies), () => []), | ||
|
||
export XueRunConfiguration = record(string(), XueRunRecipe) | ||
|
||
createConfiguration = (userConfig) -> | ||
[err, data] = validate userConfig, XueRunConfiguration | ||
if err then throw err | ||
return create data, XueRunConfiguration | ||
|
||
export default createConfiguration |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { parse } from "https://deno.land/[email protected]/flags/mod.ts"; | ||
import { parse as parseYAML } from "https://deno.land/[email protected]/encoding/yaml.ts" | ||
import { YAMLError } from "https://deno.land/[email protected]/encoding/_yaml/error.ts" | ||
import { printf } from "https://deno.land/[email protected]/fmt/printf.ts" | ||
import { StructError } from "https://esm.sh/superstruct" | ||
import { runRecipe } from "./core.coffee" | ||
import createConfiguration from "./schema.coffee" | ||
|
||
loadXueRunTasks = (path) -> | ||
try | ||
content = Deno.readTextFileSync(path) | ||
return createConfiguration(parseYAML(content)) | ||
catch error | ||
if error instanceof YAMLError or error instanceof StructError | ||
console.error( | ||
"\nxuerun: oops, invalid .xuerun tasks.\nError:", error.message, "\n") | ||
else console.error( | ||
"\nxuerun: oops, can't read .xuerun tasks.\nError:", error.message, "\n") | ||
Deno.exit(1) | ||
|
||
printVersion = () -> | ||
console.log() # print padding | ||
ascii = [ | ||
'y88b / 888~-_ ' | ||
' y88b / 888 888 e88~~8e 888 \\ 888 888 888-~88e' | ||
' y88b/ 888 888 d888 88b 888 | 888 888 888 888' | ||
' /y88b 888 888 8888__888 888 / 888 888 888 888' | ||
' / y88b 888 888 y888 , 888_-~ 888 888 888 888' | ||
'/ y88b "88_-888 "88___/ 888 ~-_ "88_-888 888 888'].join("\n") | ||
console.info("%s\n", ascii) | ||
printf("XueRun v%s ( %s / %s )\n", "0.1.0", Deno.build.os, Deno.build.arch) | ||
currentYear = new Date().getFullYear() | ||
printf("(c) 2022%s Hein Thant Maung Maung. Licensed under BSD-2-CLAUSE.\n\n", | ||
if currentYear == 2022 then "" else " - #{currentYear}") | ||
|
||
printHelp = (shouldPrintVersion = true) -> | ||
if shouldPrintVersion then printVersion() | ||
helpStrings = [ | ||
"General:", | ||
" xuerun [tasks]... [options]...", | ||
"", | ||
"Options:", | ||
" -t, --tasks path to xuerun tasks ( default: tasks.xuerun ).", | ||
" -n, --recon do nothing, print commands." | ||
"", | ||
" -v, --version print xuerun version and others.", | ||
" -h, --help print this help message.", | ||
"", | ||
"For docs, usage, etc., visit https://github.com/heinthanth/xuerun."].join("\n") | ||
console.info(helpStrings, "\n") | ||
|
||
actionKind = | ||
RUN_RECIPE: "RUN_RECIPE" | ||
PRINT_HELP: "PRINT_HELP" | ||
PRINT_VERSION: "PRINT_VERSION" | ||
|
||
parseCmdOption = () -> | ||
{'_': positional, '--': cliRest, ...options} = parse Deno.args, { "--": true } | ||
userOption = | ||
action: actionKind.RUN_RECIPE, | ||
recon: !1, | ||
recipes: [] | ||
options: {} | ||
tasksPath: "tasks.xuerun" | ||
|
||
# parse options | ||
Object.entries(options).forEach ([k, v]) -> switch k | ||
when "h", "help" then userOption.action = actionKind.PRINT_HELP | ||
when "v", "version" then userOption.action = actionKind.PRINT_VERSION | ||
when "t", "tasks" then userOption.tasksPath = | ||
if typeof v == "string" or typeof v == "number" then v.toString() else "tasks.xuerun" | ||
when "n", "recon" then userOption.recon = true | ||
|
||
# remove default CLI arguments | ||
{h, v, t, n, help, version, tasks, recon, ...restCLIargs} = options | ||
# parse options passed with -- --something | ||
{'_': _restPositional, ...optionsFromCLIrestOptions} = parse(cliRest) | ||
# combine options | ||
userOption.options = { ...restCLIargs, ...optionsFromCLIrestOptions } | ||
return { ...userOption, recipes: positional } | ||
|
||
programMain = () -> | ||
{recipes, tasksPath, action, options, recon} = parseCmdOption() | ||
if action == actionKind.PRINT_HELP then return printHelp() | ||
if action == actionKind.PRINT_VERSION then return printVersion() | ||
|
||
# load and run | ||
xueRunRc = loadXueRunTasks(tasksPath) | ||
if recipes.length == 0 | ||
if xueRunRc.hasOwnProperty("all") then return runRecipe(xueRunRc, recipes, userOption) | ||
else console.error("\nxuerun: oops, no recipe given, nothing to do!\n"); Deno.exit(1) | ||
recipes.forEach (recipe) -> runRecipe(xueRunRc, recipe, options, recon, !1) | ||
|
||
# call main function | ||
if import.meta.main then programMain() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run --allow-env | ||
|
||
import { compile } from "https://esm.sh/coffeescript"; | ||
import { join, parse, format } from "https://deno.land/[email protected]/path/mod.ts"; | ||
|
||
const __dirname = new URL(".", import.meta.url).pathname; | ||
const compiler = join(__dirname, "build.coffee"); | ||
const { base: _base, ext: _ext, ...rest } = parse(compiler); | ||
const compilerOut = format({ ...rest, name: "tmp_", ext: ".js" }); | ||
|
||
await Deno.writeTextFile(compilerOut, compile(await Deno.readTextFile(compiler))); | ||
|
||
const cmd = ["deno", "run", "--allow-read", | ||
"--allow-write", "--allow-env", "--allow-run", compilerOut]; | ||
const p = Deno.run({ cmd, stderr: "inherit" }); | ||
|
||
const processStatus = await p.status(); | ||
await Deno.remove(compilerOut); | ||
(processStatus.code != 0) && Deno.exit(processStatus.code); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { compile } from "https://esm.sh/coffeescript" | ||
import { walkSync, ensureDirSync, emptyDir } from "https://deno.land/[email protected]/fs/mod.ts" | ||
import { join, parse, format } from "https://deno.land/[email protected]/path/mod.ts" | ||
|
||
__dirname = new URL(".", import.meta.url).pathname | ||
srcPath = join(__dirname, "..", "src") | ||
distPath = join(__dirname, "..", "tmp") | ||
binPath = join(__dirname, "..", "bin", "xuerun") | ||
mainMod = join(distPath, "xuerun.js") | ||
|
||
await emptyDir(distPath) | ||
|
||
Array(...walkSync(srcPath)).forEach (p) -> | ||
dist = p.path.replace(srcPath, distPath) | ||
if (p.isDirectory) then return ensureDirSync(p.path) | ||
content = Deno.readTextFileSync(p.path) | ||
{ base: _base, ext: _ext, ...d } = parse(dist) | ||
compiledCode = compile(content, { bare: true }).replace(/\.coffee/g, ".js") | ||
Deno.writeTextFileSync(format({ ...d, ext: ".js" }), compiledCode) | ||
|
||
cmd = ["deno", "compile", "--allow-read", "--allow-write", | ||
"--allow-env", "--allow-run", "--unstable", "--output", binPath, mainMod] | ||
p = Deno.run({ cmd }) | ||
|
||
processStatus = await p.status() | ||
await Deno.remove(distPath, { recursive: true }); | ||
(processStatus.code != 0) and Deno.exit(processStatus.code); |