Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Scripts which have utility across many repositories.

This can be used either as a command `npx github:commitd/scripts command arguments --options` or you can use it interactively (REPL) by omitting the SCRIPT name `npx github:commitd/scripts`.

Note that the `python` and `shell` commands have limitations in REPL mode. You must quote your options:

- `npx github:commitd/scripts python script --option` when using command line.
- but `npx github:commitd/scripts` then `python script "--option"` in REPL.

## Use

Exact use depends on the script implementation language.
Expand All @@ -13,12 +20,7 @@ If the script is Node based (in the `src` directory) then it can be run through

# Via npx

npx github:commitd/scripts SCRIPT args

# Via npm with clone:

git clone https://github.com/commitd/scripts.git
npm run SCRIPT
npx github:commitd/scripts command args

# Or if you are developing a node project:

Expand All @@ -34,7 +36,7 @@ If the script is Python then you will need to clone it and install requirements:
```bash
# Use the npx shortcut
# Note the argument are in quotes!
npx github:commitd/scripts python SCRIPT "your_arguments --your-options "
npx github:commitd/scripts python SCRIPT


# Or clone and use directly
Expand All @@ -50,7 +52,7 @@ If the script is Python then you will need to clone it and install requirements:
```bash
# Use via npx shortcut
# Note argument and options are in quotes
npx github:commitd/scripts -c shell SCRIPT "your_arguments --your-options "
npx github:commitd/scripts shell SCRIPT your_arguments --your-options

# Or clone and run directly:

Expand Down
11 changes: 6 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 62 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,59 @@
import { Command, program } from "bandersnatch"
import { homedir } from "os"
import { join } from "path"
import { argv } from "process"
import { exampleCommand } from "./example-command"
import { pythonCommand } from "./py-command"
import { shellCommand } from "./shell-command"
import { pythonCommand, runPython } from "./py-command"
import { runShell, shellCommand } from "./shell-command"
import { sonarCommand } from "./sonar-command"
import { launch } from "./utils"
import { logger } from "./utils"

async function launch(
name: string,
description: string,
...commands: Command[]
): Promise<unknown> {
const p = program({
description,
version: true,
historyFile: join(homedir(), `.commitd_script_${name}_history`),
help: true,
prompt: `@commitd/scripts ${name} > `,
}).default(
new Command("hello").action(() =>
logger.info("Welcome from Committed Scripts, use `help` to get started")
)
)

for (const c of commands) {
p.add(c)
}

// Bandersnatch enforces strict mode, (don't let us have options it doesn't know about).
// Therefore it's not possible to pass options to Python / Bash scripts
// as they aren't part of the known options here.
// So we basically skip bandersnatch in these two command special cases
// Note that repl still requires quoting, etc

if (argv.length >= 3) {
const command = argv[2]
if (command === "python" || command === "py") {
return runPython({
script: argv[3],
"script-arguments": argv.slice(4),
"dry-run": argv.includes("--dry-run"),
})
} else if (command === "shell" || command === "sh") {
return runShell({
script: argv[3],
"script-arguments": argv.slice(4),
"dry-run": argv.includes("--dry-run"),
})
}
}

return p.runOrRepl()
}

launch(
"index",
Expand All @@ -12,3 +63,11 @@ launch(
sonarCommand,
exampleCommand
)
.then(() => {
logger.debug("Done.")
})
.catch((err) => {
logger.error("An error occurred")
logger.error(err)
process.exit(1)
})
104 changes: 59 additions & 45 deletions src/py-command/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,71 @@ export const pythonCommand = newCommand(
variadic: true,
})
.action(async (options) => {
logger.debug(options)
return runPython(options)
})

const scriptExecutable = findFileInPackage(
PYTHON_DIR,
options.script,
`${options.script}.py`
)
type PythonOptions = {
script: string
"script-arguments": string[]
"dry-run": boolean
}

if (!scriptExecutable) {
logger.error("Script %s does not exist", options.script)
return 1
}
export async function runPython(options: PythonOptions): Promise<number> {
logger.debug(options)

const pipRequirements = findFileInPackage(
PYTHON_DIR,
options.script,
`requirements.txt`
)
const scriptExecutable = findFileInPackage(
PYTHON_DIR,
options.script,
`${options.script}.py`
)

if (pipRequirements) {
if (options["dry-run"]) {
logger.info("DRY-RUN: Found pip requirements, but not installing")
} else {
const pip = await exec("pip3", [
"install",
"-r",
pipRequirements,
// Try to work without interaction
"--exists-action",
// ignore
"i",
])
if (pip.exitCode !== 0) {
logger.warn("pip returns non-zero exist code %d", pip.exitCode)
}
}
}
if (!scriptExecutable) {
logger.error("Script %s does not exist", options.script)
return 1
}

const pipRequirements = findFileInPackage(
PYTHON_DIR,
options.script,
`requirements.txt`
)

logger.info(
"Running python: `%s %s`",
options.script,
options["script-arguments"].join(" ")
)
logger.debug("Command run from %s", scriptExecutable)
if (pipRequirements) {
if (options["dry-run"]) {
logger.info("DRY-RUN: Not running python")
logger.info("DRY-RUN: Found pip requirements, but not installing")
} else {
const r = await exec("python3", [
scriptExecutable,
...options["script-arguments"],
const pip = await exec("pip3", [
"install",
"-r",
pipRequirements,
// Try to work without interaction
"--exists-action",
// ignore
"i",
])
logger.info("Python completed with exit code %d", r.exitCode)
if (pip.exitCode !== 0) {
logger.warn("pip returns non-zero exist code %d", pip.exitCode)
}
}
})
}

logger.info(
"Running python: `%s %s`",
options.script,
options["script-arguments"].join(" ")
)
logger.debug("Command run from %s", scriptExecutable)
if (options["dry-run"]) {
logger.info("DRY-RUN: Not running python")

return 0
} else {
const r = await exec("python3", [
scriptExecutable,
...options["script-arguments"],
])
logger.info("Python completed with exit code %d", r.exitCode)

return r.exitCode
}
}
64 changes: 38 additions & 26 deletions src/shell-command/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,44 @@ export const shellCommand = newCommand(
variadic: true,
})
.action(async (options) => {
logger.debug(options)
return runShell(options)
})

const scriptExecutable = findFileInPackage(
SHELL_DIR,
options.script,
`${options.script}.sh`
)
type ShellOptions = {
script: string
"script-arguments": string[]
"dry-run": boolean
}

if (!scriptExecutable) {
logger.error("Script %s does not exist", options.script)
return 1
}
export async function runShell(options: ShellOptions): Promise<number> {
logger.debug(options)

logger.info(
"Running shell: `%s %s`",
options.script,
options["script-arguments"].join(" ")
)
logger.debug("Command run from %s", scriptExecutable)
if (options["dry-run"]) {
logger.info("DRY-RUN: Not running shell command")
} else {
const r = await exec("bash", [
scriptExecutable,
...options["script-arguments"],
])
logger.info("Shell completed with exit code %d", r.exitCode)
}
})
const scriptExecutable = findFileInPackage(
SHELL_DIR,
options.script,
`${options.script}.sh`
)

if (!scriptExecutable) {
logger.error("Script %s does not exist", options.script)
return 1
}

logger.info(
"Running shell: `%s %s`",
options.script,
options["script-arguments"].join(" ")
)
logger.debug("Command run from %s", scriptExecutable)
if (options["dry-run"]) {
logger.info("DRY-RUN: Not running shell command")
return 0
} else {
const r = await exec("bash", [
scriptExecutable,
...options["script-arguments"],
])
logger.info("Shell completed with exit code %d", r.exitCode)
return r.exitCode
}
}
37 changes: 1 addition & 36 deletions src/utils/cli.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { command, Command, program } from "bandersnatch"
import { logger } from "./logger"
import { join } from "path"
import { homedir } from "os"
import { command } from "bandersnatch"

/**
* Creates a new command with standard options
Expand All @@ -17,35 +14,3 @@ export function newCommand(name: string, description: string) {
default: false,
})
}

export function launch(
name: string,
description: string,
...commands: Command[]
): void {
const p = program({
description,
version: true,
historyFile: join(homedir(), `.commitd_script_${name}_history`),
help: true,
prompt: `@commitd/scripts ${name} > `,
}).default(
new Command("hello").action(() =>
logger.info("Welcome from Committed Scripts, use `help` to get started")
)
)

for (const c of commands) {
p.add(c)
}

p.runOrRepl()
.then(() => {
logger.debug("Done.")
})
.catch((err) => {
logger.error("An error occurred")
logger.error(err)
process.exit(1)
})
}
2 changes: 1 addition & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { logger } from "./logger"
export { launch, newCommand } from "./cli"
export { newCommand } from "./cli"
export { DEFAULT_EXEC_OPTIONS, ExecOptions, exec } from "./exec"
export { findFileInPackage } from "./package"
export * from "./constants"
Loading