diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2024-03-10 14:59:14 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2024-03-10 14:59:14 +0100 |
| commit | 1100562e29f6476448b656dbddd4cf22505523f6 (patch) | |
| tree | 442eec492199104bd49dfd74474ce89ade8fcac9 /content/posts/2023-05-31-re-inventing-task-runner-that-i-actually-used-daily.md | |
| parent | a40d80be378e46a6c490e1b99b0d8f4acd968503 (diff) | |
| download | mitjafelicijan.com-1100562e29f6476448b656dbddd4cf22505523f6.tar.gz | |
Move back to JBMAFP
Diffstat (limited to 'content/posts/2023-05-31-re-inventing-task-runner-that-i-actually-used-daily.md')
| -rw-r--r-- | content/posts/2023-05-31-re-inventing-task-runner-that-i-actually-used-daily.md | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/content/posts/2023-05-31-re-inventing-task-runner-that-i-actually-used-daily.md b/content/posts/2023-05-31-re-inventing-task-runner-that-i-actually-used-daily.md new file mode 100644 index 0000000..b311509 --- /dev/null +++ b/content/posts/2023-05-31-re-inventing-task-runner-that-i-actually-used-daily.md | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | --- | ||
| 2 | title: "Re-Inventing Task Runner That I Actually Used Daily" | ||
| 3 | url: /re-inventing-task-runner-that-i-actually-used-daily.html | ||
| 4 | date: 2023-05-31T12:21:10+02:00 | ||
| 5 | type: post | ||
| 6 | draft: false | ||
| 7 | --- | ||
| 8 | |||
| 9 | Couple of months ago I had this brilliant idea of re-inventing the wheel by | ||
| 10 | making an alternative for make. And so I went. Boldly into the battle. And to my | ||
| 11 | big surprise my attempt resulted in not a completely useless piece of software. | ||
| 12 | |||
| 13 | My initial requirements were quite simple but soon grow into something more | ||
| 14 | ambitious. And looking back I should have stuck to the simple version. My | ||
| 15 | laziness was on my side this time though. Because I haven’t implemented some of | ||
| 16 | the features I now realise I really didn’t need them and they would bog the | ||
| 17 | whole program and make it be something it was never meant to be. | ||
| 18 | |||
| 19 | My basic requirements were following: | ||
| 20 | |||
| 21 | - Syntax should be a tiny bit inspired by Rake and Rakefiles. | ||
| 22 | - Should borrow the overall feel of a unit test experience. | ||
| 23 | - Using something like Python would be a bit of an overkill. | ||
| 24 | - The program must be statically compiled, so it can run on same architecture | ||
| 25 | without libc, musl dependencies or things like that. | ||
| 26 | - Install ruby for rake is a bit overkill and can not be done with certain | ||
| 27 | really lightweight distributions like Alpine Linux. This tool would be usable | ||
| 28 | on such lightweight systems for remote debugging. | ||
| 29 | - I want to use it for more than just compiling things. I want to use it as an | ||
| 30 | entry-point into a project, and I want this to help me indirectly document the | ||
| 31 | project as well. | ||
| 32 | - It should be an abstraction over bash shell or the default system shell. | ||
| 33 | - Each task essentially becomes its own shell instance. | ||
| 34 | - Must work on Linux and macOS systems. | ||
| 35 | - By default, running `erd` list all the available tasks (when I use make, I | ||
| 36 | usually put a disclaimer that you should check Makefile to see all available | ||
| 37 | target). | ||
| 38 | - Should support passing arguments when you run it from a shell. | ||
| 39 | - Normal variable as the same as environmental variables. There is no | ||
| 40 | distinction. Every variable is also essentially an environment variable and | ||
| 41 | can be used by other programs. | ||
| 42 | - State between tasks is not shared, and this makes this “pure” shell instances. | ||
| 43 | - Should be single-threaded for the start and later expanded with `@spawn` | ||
| 44 | command. | ||
| 45 | - Variables behave like macros and are preprocessed before evaluation. | ||
| 46 | - Should support something like `assure` that would check if programs like C | ||
| 47 | compiler or Python (whatever the project requires) are installed on a machine. | ||
| 48 | |||
| 49 | Quite a reasonable list of requirements. I do this things already in my | ||
| 50 | Makefiles or/and Bash scripts. But I would like to avoid repeating myself every | ||
| 51 | time I start working on something new. | ||
| 52 | |||
| 53 | So I started with the following syntax. | ||
| 54 | |||
| 55 | ```ruby | ||
| 56 | @env on | ||
| 57 | |||
| 58 | # Override the default shell. | ||
| 59 | @shell /bin/bash | ||
| 60 | |||
| 61 | # Assure that program is installed. | ||
| 62 | @assure docker-compose pip python3 | ||
| 63 | |||
| 64 | # Load local dotenv files (these are then globally available). | ||
| 65 | @dotenv .env | ||
| 66 | @dotenv .env.sample | ||
| 67 | @dotenv some_other_file | ||
| 68 | |||
| 69 | # This are local variables but still accessible in tasks. | ||
| 70 | @var HI = "hey" | ||
| 71 | @var TOKEN = "sometoken" | ||
| 72 | @var EMAIL = "m@m.com" | ||
| 73 | @var PASSWORD = "pass" | ||
| 74 | @var EDITOR = "vim" | ||
| 75 | |||
| 76 | @task dev "Test chars .:'}{]!//" does | ||
| 77 | echo "..." $HI | ||
| 78 | end | ||
| 79 | |||
| 80 | @task clean "Cleans the obj files" does | ||
| 81 | rm .obj | ||
| 82 | end | ||
| 83 | |||
| 84 | @task greet "Greets the user" does | ||
| 85 | echo "Hi user $TOKEN or $WINDOWID $EMAIL" | ||
| 86 | end | ||
| 87 | |||
| 88 | @task stack "Starts Docker stack" does | ||
| 89 | docker-compose -f stack.yml up | ||
| 90 | end | ||
| 91 | |||
| 92 | @task todo "Shows all todos in source files and count them" does | ||
| 93 | grep -ir "TODO|FIXME" . | wc -l | ||
| 94 | end | ||
| 95 | |||
| 96 | @task test1 "For testing 1" does | ||
| 97 | unknown-command | ||
| 98 | echo "test1" | ||
| 99 | ls -lha | ||
| 100 | end | ||
| 101 | |||
| 102 | @task test2 "For testing 2" does | ||
| 103 | echo "test1" | ||
| 104 | ls -lha | ||
| 105 | docker-compose -f samples/stack.yml up | ||
| 106 | end | ||
| 107 | ``` | ||
| 108 | |||
| 109 | One thing that I really like about Errand. Yes, this is what it is called. And | ||
| 110 | it is available at https://git.mitjafelicijan.com/errand.git/about/. Moving | ||
| 111 | on. One thing that I really like is that a task is a persistent shell. By that I | ||
| 112 | mean, that the whole task, even if it contains multiple command in one shell. | ||
| 113 | In make each line in a target is that and you need to combine lines or add `\` | ||
| 114 | at the end of the line. | ||
| 115 | |||
| 116 | ```bash | ||
| 117 | # How you do this things in make. | ||
| 118 | target: | ||
| 119 | source .venv/bin/activate \ | ||
| 120 | python script.py | ||
| 121 | ``` | ||
| 122 | |||
| 123 | This solves this problem. Consider each task and what is being executed in that | ||
| 124 | task a shell that will only close when all the tasks are completed. | ||
| 125 | |||
| 126 | By self-documenting I mean that if you are in a directory with `Errandfile` in, | ||
| 127 | if you only type `erd` and press enter it should by default display all the | ||
| 128 | possible targets. In make i was doing this by having a first target be something | ||
| 129 | like `default` that echos the message “Check Makefile for all available target.” | ||
| 130 | Because all of the tasks in Errand require a message I use that to display let’s | ||
| 131 | call it table of contents. | ||
| 132 | |||
| 133 | Because I don’t use any external dependencies this whole thing can be statically | ||
| 134 | compiled. So that also checked one of the boxes. | ||
| 135 | |||
| 136 | It works on Linux and on a Mac so that’s also a bonus. I don’t believe this | ||
| 137 | would work on Windows machines because of the way that I use shell instances. By | ||
| 138 | you could use something like Windows Subsystem for Linux and run it in | ||
| 139 | there. That is a valid option. | ||
| 140 | |||
| 141 | To finish this essay off, how was it to use it in “real life”. I have to be | ||
| 142 | honest. Some of the missing features still bother me. `@dotenv` directive is | ||
| 143 | still missing and I need to implement this ASAP. | ||
| 144 | |||
| 145 | Another thing that needs to happen is support for streaming output. Currently | ||
| 146 | commands like `docker-compose` that runs in foreground mode is not compatible | ||
| 147 | with Errand. So commands that stream output are an issue. I need to revisit how | ||
| 148 | I initiate shell and how I read stdout and stderr. But that shouldn’t be a | ||
| 149 | problem. | ||
| 150 | |||
| 151 | I have been very satisfied with this thing. I am pleasantly surprised by how | ||
| 152 | useful it is. I really wanted to test this in the wild before I commit to it. I | ||
| 153 | have more abandoned project than Google and it’s bringing a massive shame to my | ||
| 154 | family at this point. So I wanted to be sure that this is even useful. And it | ||
| 155 | actually is. Quite surprised at myself. | ||
| 156 | |||
| 157 | I really need to package this now and write proper docs. And maybe rewrite | ||
| 158 | tokeniser. Its atrocious right now. Site to behold! But that is an issue for | ||
| 159 | another time. | ||
