Code Monkey home page Code Monkey logo

igor-petruk / scriptisto Goto Github PK

View Code? Open in Web Editor NEW
915.0 13.0 25.0 274 KB

A language-agnostic "shebang interpreter" that enables you to write scripts in compiled languages.

License: Apache License 2.0

Rust 81.47% C 1.63% Scheme 0.26% C++ 0.67% Crystal 0.62% TypeScript 0.61% Elixir 0.91% Fortran 0.39% Go 0.76% Haskell 0.33% Java 1.60% Pascal 0.33% Python 2.59% Racket 0.46% Common Lisp 0.75% JavaScript 1.06% OCaml 1.64% Reason 1.67% Zig 0.42% Dart 1.83%
scripting unix linux rust

scriptisto's Introduction

Scriptisto

Latest Version Build Status Crates.io License Libraries.io dependency status for latest release GitHub top language

Crates.io GitHub All Releases

Packaging status

It is tool to enable writing one file scripts in languages that require compilation, dependencies fetching or preprocessing.

It works as a "shebang" for those scripts, extracting build instructions from comments. If a script is changed, scriptisto rebuilds it and caches the result. If a script was already built, scriptisto immediately delegates to a binary with only <1 ms overhead.

Builds in Docker without installing a compiler on a host system are possible.

Advantages and use-cases are listed in the Wiki.

Demo

#!/usr/bin/env scriptisto

#include <stdio.h>
#include <glib.h>

// scriptisto-begin
// script_src: main.c
// build_cmd: clang -O2 main.c `pkg-config --libs --cflags glib-2.0` -o ./script
// scriptisto-end

int main(int argc, char *argv[]) {
  gchar* user = g_getenv("USER");
  printf("Hello, C! Current user: %s\n", user);
  return 0;
}
$ chmod +x ./script.c
$ ./script.c
Hello, C! Current user: username

Note: some templates such as rust take more time to build during the first time. The default behaviour is to supress the build logs, so do not be discouraged that you do not immediately see any output. More info in the wiki.

Installation

Scriptisto is available as a prebuilt statically-linked standalone binary or distributions packages at Releases or at Crates.io.

Please proceed to the Installation for instructions.

Documentation

Proceed to our Wiki.

Disclaimer

This is not an officially supported Google product.

scriptisto's People

Contributors

abduelhamit avatar afazekas avatar alexkintis avatar andidog avatar carltoffel avatar chipschipschips avatar dependabot[bot] avatar igor-petruk avatar jayvdb avatar lordmzte avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scriptisto's Issues

Add "build_cmd_once"

This will run only during the first build, before build_cmd. If we are rebuilding the binary, this won't run again. We can add "scriptisto cache invalidate" command to force it to rerun.

Use cases:

  • Setup development environment, like virtualenv+pip for Python or "dotnet restore"
  • Fetch extra files from the web via curl
  • Prebuild something heavy that is rarely to change

Golang 1.16 support

The following script works. In short, nix-shell installs scriptisto (a custom Nix derivation that I made that is not available on the Nix Package Registry) and go_1_15, and then uses scriptisto to launch the script as a C++ program:

#!/usr/bin/env nix-shell
/*
#!nix-shell -i scriptisto -p scriptisto go_1_15
*/

// scriptisto-begin
// script_src: main.go
// build_once_cmd: go get github.com/fatih/color
// build_cmd: go build -o script
// replace_shebang_with: //
// scriptisto-end

package main

import "github.com/fatih/color"

func main() {
  color.Yellow("Hello, Go!")
}

However, if I change go_1_15 to go_1_16, then the script no longer works, and produces the following error:

go: go.mod file not found in current directory or any parent directory; see 'go help modules'
Error: ErrorMessage { msg: "Command \"/bin/sh\" \"-c\" \"go build -o script\" failed. Exit code: 1." }

This is because since Go 1.16, the GO111MODULE default has changed from auto to on. See: https://go.dev/blog/go116-module-changes

If I manually run export GO111MODULE=auto before executing the scriptisto program, then the program works again. It would be great if scriptisto had an option to set environment variables before compiling and executing the program, e.g. add-env: GO111MODULE=auto.

A workaround is to use nix-shell itself to set the variable, but that can get cumbersome and may inadvertently be applied to more than one environment: https://stackoverflow.com/questions/62246831/setting-environment-variables-in-nix-via-a-shebang

A configuration that works for Go 1.16, is to make use of the files option to create a go.mod file:

#!/usr/bin/env nix-shell
/*
#!nix-shell -i scriptisto -p scriptisto go_1_16
*/

// scriptisto-begin
// script_src: main.go
// build_cmd: go mod tidy && go build -o script
// replace_shebang_with: //
// files:
//  - path: go.mod
//    content: |
//      module github.com/a/b
//      go 1.17
//      require (
//          github.com/fatih/color v1.13.0
//      )
// scriptisto-end

package main

import "github.com/fatih/color"

func main() {
  color.Yellow("Hello, Go!")
}

Related issues:

  1. #34

Feature request: Showing build progress indicator and/or build command output

I just tried to execute the default Rust template, and it took a long time to build. The problem is, that there is no indicator of what is happening. I think, there should at least be a message like "Building XY, this could take a few seconds...").

It's easy to add this functionality with a custom 'build_cmd' value, but I thought it would be useful to provide some feedback regarding something that potentially is confusing to end-users (end-users of a script). If they have to wait 30 seconds until something happens, then some users most likely will be confused a bit.

A progress indicator would be ideal, but difficult to implement. An option to show a custom message before a build would also solve the problem.

Personally, I'd like to always see the full build output, but it seems that's not possible at the moment.

Cannot excecute go scripts

Expected Behavior

Execute go script

Actual Behavior

go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.

Steps to Reproduce the Problem

Create an go template and try to run it.

Specifications

  • Version: v2.1.1
  • Platform: 6.9.3-arch1-1

Docker-Rust template fails

Expected Behavior

Simply running the example template for docker-rust should succeed.

Actual Behavior

Running the example yields in an error.

cp: can't stat '/vol/./target/x86_64-unknown-linux-musl/release/script': No such file or directory

Error: Build failed for "/Users/albocc/test/baz.rs"

Caused by:
    Command cd "/Users/albocc/Library/Caches/scriptisto/bin/Users/albocc/test/baz.rs" && "docker" "run" "-t" "--rm" "-u" "501" "-v" "scriptisto-baz.rs-59a389cedb23bfb1783c7e9877873f89-src:/vol" "-v" "/Users/albocc/Library/Caches/scriptisto/bin/Users/albocc/test/baz.rs:/src" "busybox" "sh" "-c" "mkdir -p $(dirname /src/./target/x86_64-unknown-linux-musl/release/script) && cp -rf /vol/./target/x86_64-unknown-linux-musl/release/script /src/./target/x86_64-unknown-linux-musl/release/script" failed. Exit code: 1.

Steps to Reproduce the Problem

  1. cargo install scriptisto
  2. scriptisto new docker-rust > baz.rs
  3. chmod +x baz.rs
  4. ./baz.rs

Specifications

  • Version: v2.1.1
  • Platform: macOS 14.3.1 (23D60)
  • Cargo: cargo 1.76.0 (c84b36747 2024-01-18)
  • Rustc: rustc 1.76.0 (07dca489a 2024-02-04)

Any way to get the original script's filename/location?

For example, in Rust we can normally write std::file!() to get the source file path/name, but this will just return something like src/main.rs because of the way the build is done. It would be nice to have a way to get the actual name/location of the original script--this is an important part of several shell scripts that I'd like to eventually replace.

Installation with cargo fails for version 0.6.2

Expected Behavior

Installation should succeed without error.

Actual Behavior

Installation with cargo install fails with error message:

error[E0599]: no function or associated item named `new` found for type `scrawl::editor::Editor<_>` in the current scope
   --> /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/scriptisto-0.6.2/src/templates.rs:246:46
    |
246 |     let mut editor = scrawl::editor::Editor::new();
    |                                              ^^^ function or associated item not found in `scrawl::editor::Editor<_>`

error: aborting due to previous error

Steps to Reproduce the Problem

  1. Run cargo install scriptisto

Specifications

  • Version: 0.6.2
  • Platform: Fedora Linux

Add csharp template

I got a C# template on .NET working with scriptisto.

Is it ok if I do a pull request to add it?

docs/design: document windows support or use PE file for Windows/DOS and shebang hack

For example with zig executable in PATH (but also without and given the path to the executable) I can do:

//usr/bin/env zig run --global-cache-dir "/tmp/zigruns" --enable-cache "$0" -- "$@" | tail -n +2; exit
// ^
// for no cache dir printed.
// with cache dir:
//usr/bin/env zig run --global-cache-dir "/tmp/zigruns" --enable-cache "$0" -- "$@"; exit
// without cache dir:
//usr/bin/env zig run "$0" -- "$@"; exit

// Only works with files with file ending .zig

pub fn main() void {
    @import("std").debug.print("hello there\n", .{});
}

and chmod +x ./example.zig && ./example.zig just works. Likewise one can use the cache to compile C and C++ programs and afaik newer compiled languages all come up with their own caching system.

I would assume that Rust has a similar option to let the user set the cache directory?

Note, that this shebang hack is also used by https://justine.lol/ape.html by building "naked" thompson shell script without a shebang line that works as a PE file for Windows/DOS and use exec to feed a binary to the shell via pipe.
You could copy-paste that approach, if there is currently no Windows support.

Executable not found, show-stopper failure since 0.6.13

Expected Behavior

$ scriptisto -V
scriptisto 0.6.10
$ for lang in c cpp elixir rust; do scriptisto new $lang > hello && chmod +x hello && ./hello; done
Hello, C! Current user: work
Hello, C++! Current user: work
Hello, Elixir!
Hello, Rust! Command line options: Opt { input: None }
$ ./hello -i foo
Hello, Rust! Command line options: Opt { input: Some("foo") }

Actual Behavior

$ scriptisto -V
scriptisto 0.6.13
$ for lang in c cpp elixir rust; do scriptisto new $lang > hello && chmod +x hello && ./hello; done
Error: ErrorMessage { msg: "\"./hello\" is not found or not executable" }
Error: ErrorMessage { msg: "\"./hello\" is not found or not executable" }
Error: ErrorMessage { msg: "\"./hello\" is not found or not executable" }
Error: ErrorMessage { msg: "\"./hello\" is not found or not executable" }
$ ./hello -i foo
error: Found argument '-i' which wasn't expected, or isn't valid in this context

USAGE:
    scriptisto [ARGS] [SUBCOMMAND]

For more information try --help

Steps to Reproduce the Problem

  1. Update scriptisto to 0.6.13
  2. try to run any scriptisto-based file

0.6.10 is not affected, and 0.6.11/12 are apparently not available.

Specifications

  • Version: 0.6.13 (0.6.10 unaffected)
  • Platform: Linux 64 bits, cargo install

Multiple shebangs are not processed with replace_shebang_with

First of all, thanks for this project!

The following is a working example that uses nix-shell to run scriptisto as interpreter. nix-shell will install all the dependencies required to run the script (e.g. scriptisto, pkg-config, clang and glibmm) and then execute scriptisto. You can read more about that here: https://nixos.org/manual/nix/stable/command-ref/nix-shell.html#use-as-a--interpreter

Two shebangs are used, one to specify nix-shell as the script runner, and one that configures nix-shell to use scriptisto as the interpreter. The second shebang is "wrapped" in C++ comments, to avoid compile errors. Ideally, scriptisto should have an option to indicate other shebangs that also need to be replaced with comments, e.g. other_shebangs_prefixes: #!nix-shell #!some-other-prefix.

#!/usr/bin/env nix-shell
/*
#!nix-shell -i scriptisto -p scriptisto pkg-config clang_9 glibmm_2_68
*/

#include <glibmm.h>
#include <iostream>

// scriptisto-begin
// script_src: main.cpp
// build_cmd: clang++ -std=c++17 -O2 main.cpp `pkg-config --libs --cflags glibmm-2.68` -o ./script
// replace_shebang_with: //
// scriptisto-end

int main(int argc, char *argv[]) {
  const auto user = Glib::getenv("USER");
  std::cout << "Hello, C++! Current user: " << user << std::endl;
  return 0;
}

The Nix derivation that installs scriptisto looks like this:

{ pkgs }:

pkgs.stdenv.mkDerivation rec {
	pname = "scriptisto";

	version = "0.6.14";

	owner = "igor-petruk";

	src = pkgs.fetchurl {
		url = "https://github.com/${owner}/${pname}/releases/download/v${version}/${pname}.bz2";
		sha256 = "YP00pnvADuRsuzKIe988L3zI/joHfYicaQTNT4x+Q00=";
	};

	sourceRoot = ".";

	unpackCmd = ''
		bzcat $src > ./scriptisto
	'';

	nativeBuildInputs = [
		pkgs.autoPatchelfHook
		pkgs.bzip2
	];

	installPhase = ''
		runHook preInstall
		ls -al
		install -m755 -D scriptisto $out/bin/scriptisto
		runHook postInstall
	'';

	meta = with pkgs.lib; {
		license = licenses.asl20;
		homepage = "https://github.com/igor-petruk/scriptisto";
		description = "A language-agnostic shebang interpreter that enables you to write scripts in compiled languages.";
		maintainers = with maintainers; [ igor-petruk ];
	};
}

Add "template import" or "add --from-file" command

"add" command has a limited application as is, because you likely need to do some debugging first before you make something a template.

Alternatively "template run" could work, as you could iterate on what you just run, but it is problematic, as not every save in "edit" mode is stored back to the original file. Maybe "edit" should do in-place editing

Add option to remove cache after build

Would it be possible to add an option to remove all files except the built binary? For instance in Rust for even a simple script runs over 100MB, but the built binary is only a megabyte or two. Writing several small scripts can become very wasteful.

For instance, a new configuration option field named clean_cache_after_build which removes all cache files except the target_bin could be added so that excessive disk space isn't wasted.

Eliminate data race during concurrent runs that trigger rebuild

So far multiples runs of the script are unaware of each other and they can trigger compiler concurrently. This can lead to unpredicted behavior, especially if the build is multi-stage.

Something like a lock file should be considered, but has to be very low cost. Preferrably no cost for the path, where the binary is already compiled.

Add package to Homebrew

Installing it via cargo is quite slow. Having a ready binary is much better. Homebrew supports both macOS and linux.

Executing 'bin/xy.rs' runs scriptisto itself, instead of script ('./bin/xy.rs' works as expected)

Expected Behavior

Executing bin/xy.rs executes the target script.

Actual Behavior

scriptisto itself is executed.

This works:

[dh@arch]$ ./bin/cli.rs
Hello, Rust! Command line options: Opt { input: None } 

This doesn't work (notice the missing "./"):

[dh@arch]$ bin/cli.rs 
scriptisto 0.6.10
A 'shebang-interpreter' for compiled languages

USAGE:
    scriptisto [ARGS] [SUBCOMMAND]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <script-src>    A path to a script to run. If specified, first character must be "." or "/"
    <args>...       Additional arguments passed to a script

SUBCOMMANDS:
    build       Build a script without running
    cache       Build cache operations
    help        Prints this message or the help of the given subcommand(s)
    new         Prints an example starting script in a programming language of your choice
    template    Manage custom script templates
[dh@arch abalet.com]$ 

Steps to Reproduce the Problem

  1. Run: mkdir bin
  2. Run: scriptisto new rust bin/foo.rs
  3. Run: chmod +x bin/foo.rs
  4. Run: bin/foo.rs

Specifications

  • Version: scriptisto 0.6.10
  • Platform: Linux

Cannot run scripts anymore after PATH search change

Expected Behavior

$ scriptisto new rust >bin/foo.rs
$ chmod +x bin/foo.rs
$ ./bin/foo.rs
Hello, Rust! Command line options: Opt { input: None }
$ bin/foo.rs
Hello, Rust! Command line options: Opt { input: None }

(contrived output – both cases, with and without ./ prefix, must be fixed)

Actual Behavior

$ scriptisto --version # built from Git
scriptisto 0.6.14-alpha.0
$ scriptisto new rust >bin/foo.rs
$ chmod +x bin/foo.rs
$ ./bin/foo.rs
Error: ErrorMessage { msg: "\"./bin/foo.rs\" is not found or not executable" }
$ bin/foo.rs
Error: ErrorMessage { msg: "\"bin/foo.rs\" is not found or not executable" }

The fix for #26 has introduced this bug since bin/foo.rs isn't on the PATH. Searching the PATH doesn't solve the use cases. The first command line argument is a file path, potentially containing slashes, and not a name of an executable (which can never contain slashes). One could check it with Path::new("bin/foo.rs").exists(), if needed by the code to distinguish from subcommands.

Specifications

  • Version: scriptisto 0.6.14-alpha.0 (built from commit f9ef3bd)
  • Platform: macOS

Workaround

Had to revert to 0.6.10 to at least get the ./some-script syntax to work correctly.

[Feature Request]: adding a config file to avoid boilerplate

Feature request

Wouldn't it be better for the UX to parse a, for example, YAML file located in, for example, $XDG_CONFIG/scriptisto/config.yml or loading a YAML file for a variable like $SCRIPTISTO_CONFIG, and if not found or defined, use the current way of defining the per-script build_cmd.

Proposal

# path/to/config.yml
lang: "c"
  build_cmd: "clang -O2 {{ script_src }} `pkg-config --libs --cflags glib-2.0`"
lang: "go
  build_cmd: "go build {{ script_src }}"

Note

I will use TOML since it differentiates more the blocks like:

[c]
build_cmd = "clang -O2 [$script_src] `pkg-config --libs --cflags glib-2.0`"

When parsing it take the script.{c,go}, substitute it with the script_src (current file from the she-bang call) and add it to build_cmd like -o ./script

Note

I am not a Rust developer, so I don't know how complicated or not it is this environment, but starship does it to configure the prompt.

If the current way of doing things is present on the current script.{c,go} use that instead of config.yml so it has a way to change per-script for some corner cases.

In the name of this tool an Esperantism?

Really interesting project and an incredible leap forward for this kind of work.

I just really want to know if the -isto suffix was inspired by the Esperanto language.

Added to `pkgx`

I don’t usually reach out about packages I add to https://pkgx.sh but scriptisto is really neat and pkgx works well with it.

For example you can use pkgx to run scriptisto scripts and add other packages to the environment, eg. your demo script can have clang, pkg-config and glib added to the environment for users automatically using our shebang syntax.

#!/usr/bin/env -S pkgx +gnome.org/glib +pkg-config +clang scriptisto

#include <stdio.h>
#include <glib.h>

// scriptisto-begin
// script_src: main.c
// build_cmd: clang -O2 main.c `pkg-config --libs --cflags glib-2.0` -o ./script
// scriptisto-end

int main(int argc, char *argv[]) {
	gchar* user = g_getenv("USER");
	printf("Hello, C! Current user: %s\n", user);
	return 0;
}

I added this demo to mash which is our script package manager which could be a neat place for the community to make scriptisto scripts more broadly available:

$ mash demo scriptisto

Mash has a neat feature where you can run scripts without installing anything to the system via our cURL one liner:

sh <(curl https://mash.pkgx.sh) demo scriptisto

Anyway, just thought I'd post here in case you like it. If not feel free to close, otherwise I add a section to the wiki with your approval.

https://pkgx.dev/pkgs/github.com/igor-petruk/scriptisto/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.