RFC: Add Makefile to Foreman Repository

RFC: Add Foreman Repo Makefile

Context and Problem Statement

Currently there is no Makefile inside the Foreman repository. After jumping between projects I still find myself jumping back to my notes I wrote earlier this year when I first joined the project. I am sure that there are also some commands that other people use that are longer and more complex than the ones I use often.

I sometimes find it hard to remember all of these commands. This could just be a skill issue on my side .

Proposal

Add a Makefile to handle the commands, this will 1. serve as “documentation” for all these commands and 2. make it simple to run commands. If anyone forgets command names they can simply look inside the Makefile.

I don’t think there is documentation on some of these commands inside the foreman repository, you have to go to personal notes or foreman development docs.

Running commands with the Makefile is super simple I think most people know and are familiar with Makefiles.

Finally if people don’t want to use Make or want to continue manually typing out commands they will always be free to do so since Make is not forced upon anyone.

Examples
bundle exec foreman start → make start
bundle exec rake db:migrate → make db-migrate
bundle exec rake console → make console

Can you provide some examples of commands that are undocumented? Is it possible some of these are just Rails features that are documented there?

Maybe I should take back that statement, looking again I do see quite a bit of documentation inside developer_docs/foreman_dev_setup.asciidoc.

A lot of these are just native rails commands. Even with the foreman_dev_setup.asciidoc having a Makefile seems to still be a nice addition.

If I start from your examples here, what do you see as the value of make over rake within our predominantly Ruby ecosystem?

I have had a similar debate over in foremanctl if we would be best served leaning into our forge pattern, or introducing make for some actions.

I think it would benefit us a whole project to arrive at consistency, and a clear set of rules by which we determine what goes into which system.

In a world that we are going to Containers with foremanctl, I think going to make commands over bundle only make sense if we add the Container layer on top make sure that we are doing the same as we would do in a world with source based containers, with podman locally.

Please remember I am not from the ruby or rails ecosystem so my understanding rake vs make may be biased.

The way I see the value in make is

  1. Quick self documentation. Any developer can understand common commands and it will always be in the same spot every repo.
  2. make is universally used and understood by most developers.
  3. make is not language dependent. Rake seems to be language dependent. Example of this is in Katello we need to run completely different workflow to run bastion tests, which takes like 4 steps to even setup.
  4. Standardization of running commands, we have eslint, rubocop, jest, npm and more I am probably missing. make could simply standardize these to all be prefixed and possibly shortened. So Make lint could run rubocop and eslint.

My understanding is that rake is make but for ruby. To me it seems we have other languages and frameworks involved not just ruby/rails. If someone could explain why rake still makes sense in this case.

I wanted to show what I mean by self documenting. I took a look at the Rakefile in foreman and it points me to Foreman::Application.load_tasks. So I followed this and it doesn’t really tell me anything at all.

Now I looked at a repo I have never once touched or read a line of code in and I got a small understanding of dependencies and tools used in the project. I could simply read this and be aware of what is available for me to use. pytorch/Makefile at main · pytorch/pytorch · GitHub

For developers alone I can see some benefits of using make. For example, I so frequently forget how to run the UI JS unit tests for each of the backends. Having a make command for each would not only let me do it quicker but also serve as documentation for what the commands are.

Also consider that we already have a Makefile for translation bits, so we have precedent for needing one.

bundle exec foreman start is easy, but it used to have the unfortunate side effect of eating STDIN when debugging and so you’d never see what you were typing when debugging. So, we needed to run puma by hand to get around it. I’m not sure if that’s a problem anymore, but it’s another reason a rake script could help.

If we want this to be an experiment, Katello is a good place :slight_smile:

Have you looked at just? Conceptually it is very similar to good old make, but newer and it manages to avoid some of its quirks and in general I found it much easier to use without shooting yourself in the foot.

Yeah just is pretty nice as well, I used it at my old company. Downside im not sure how many people are familiar with it and its another dependency people will have to install.

Make comes on most OS that people use here, makes it a bit easier.

To Be Fair

A couple of to be fairs:

  • rake requires rubygem-rake and ruby, both of which are available everywhere
  • rake in it’s most basic form is intended to be what make but using an easier to read language and built-in niceties like listing and documentation
  • rake can call systems commands, so it’s not limited to Ruby code
  • rake can list the available commands with descriptions easily with rake -T

rake is make written in Ruby, not just for Ruby. I think that is a key distinction here, rake gives you the power of Linux and Ruby, whereas Make only gives you the Linux side. For simple things, the Ruby part is not needed most likely.

In the case of Foreman’s main Rakefile, it’s an entry point where due to the number of Rake tasks they have been organized into a directory rather than one big file.

If you look at foreman/Rakefile.dist at develop · theforeman/foreman · GitHub you’ll see a more straight forward, make looking Rakefile with tasks, dependencies, etc.

Next Steps

All that being said, I think the general premise is a sound one:

Foreman and it’s ecosystem are built using multiple languages, frameworks and tools making it hard to switch between projects and remember the commands to do similar operations. A single entrypoint across the ecosystem would reduce the time spent looking for what the right command is as contributors contribute across the community.

Then, from there it becomes a question of what is the right tool to achieve that.

I think you should expand the scope of the RFC, and start with the overall problem statement, and then your proposal being that we should use make as the common language for encapsulating development tasks.

My Opinion

With all of that being said, while I much prefer reading Rake files over Make, I do agree that make is more readily available across operating system environments, is light-weight, lacks dependencies and universal. Thus, make would get my vote.

I feel the pain, I am very much Makefile guy as well. All those years with Ruby and I sometimes call make as well.

However, what I would not like us to do is maintaining the Makefile itself. Our PRs are costly (both CICD and amount of people that eventually read them). Therefore, if it can be figured out as a generic wrapper that does not need changes too often, then why not. Could be challenging because different tools are used for different things tho.

Another option is to only provide a Makefile stub that only prints some information like:

# make

This project use rake, see README or docs/blah/blah. Examples:

rake -T
rake db:migrate
...

Just two cents, can be ignored

This reminded me how we use Makefile with Rake in hammer project: we define a general Makefile [0] that uses Rake under the hood for some commands as well as more “native” make commands, that Makefile then being used by plugins as well [1].
[0] - hammer-cli/locale/Makefile.def at master · theforeman/hammer-cli · GitHub
[1] - hammer-cli-foreman/locale/Makefile at master · theforeman/hammer-cli-foreman · GitHub

Don’t ask me why, just remembered we have this :slight_smile:

The way I see it is make fits that “not maintaining” category. Sure if we decide to go with make we will need the initial setup, but once that is complete it seems its pretty much hands off from there and doesn’t require much updating.

For these next steps like the problem statement should this be another RFC, or should I update this current thread?

I don’t really see the point of this. If you’re a Ruby developer, you should know Rake. Why not lean into an ecosystem instead of trying to hide it? Under the hood the rails"binary" will run rake tasks. So if you follow Getting Started with Rails — Ruby on Rails Guides then you’ll be guided to it.

Now I’ll admit that with our webpack setup the Foreman project is a bit non-standard. We’ve investigated using more Rails native methods (Using React with importmaps on Rails 7 and Rails 7 and new frontend approaches POC) but never fully committed. A separate discussion, but that keeps causing us pain when we try to upgrade frameworks.

You can argue a single Makefile won’t hurt, but for me the principle still stands: leaning into frameworks instead of working around them makes your life easier over time. :-1: from me.

I do not think @aidenfine argument is that we should write all of the logic in make. The logic and details should remain within the ecosystem tool. However, as we are a poly-language project there are multiple commands to do similar things sometimes in a single repository, and definitely across repositories. A common interface to kick-off common development operations can help developers move easier between projects contributing in more places with less overhead.

Let’s take some examples.

Each project has some sort of requirement to setup the initial dependencies. That might be bundle install, npm install or a custom script like setup-environment in foremanctl that has to handle python, and ansible. Having to remember for a given repository which commands do I need to run can be mental overhead compared to a project wide standard of make setup-environment.

Each project has linting. Depending on what is being worked on a developer might need to run rubocop (directly or through a rake task), npm lint, pylint, ruff, stylelint. Again, having to remember, or read each time you swap context which one(s) do I need to run can be draining. Remembering that there is a make lint across our projects that will run all applicable linters for a given project reduces that and can make contributing easier.

Look at what Foreman core CI runs:

bundle exec rubocop -P

npm install --package-lock-only --no-audit
npm ci --no-audit
npm run lint
npm run stylelint
npm run test
npm run test:lint-rules
npm run test:stylelint
npm run test:plugins <plugin>

bin/rake test:units
bin/rake test:functionals
bin/rake test:graphql
bin/rake test:integration
bin/rake test:external
bin/rake webpack:compile
bin/rake apipie:cache

That’s a lot to remember, or to go read, every time. I am guilty of just using CI to do it for me because trying to remember all of this, or find the information is a pain. And yes, our docs can and should be clearer. I do not think that negates the common interface argument.

Some things I noted in an offline discussion.

There is a cost to maintainers: even if they don’t use these, they still need to maintain them.

Are you also going to unify everything? Foreman, Pulp and Candlepin each have their own way of doing things.

Makefiles are also hard to discover: you either need to read them or manually maintain some make help.

CI is a poor example because we split things to parallelize things.

There is a cost to maintainers: even if they don’t use these, they still need to maintain them.

Every change comes with the cost of maintenance, which does not mean we should avoid making the right changes … however would like to understand at what level of maintenance is this? Once a month/too frequent/Release level ..etc ?

My take on this is that make could come in handy for the places in our repositories where we have special groups of commands that run together to perform a task that is otherwise repetitive. We already have make in the locale directory of Katello to perform translation related things. The maintenance of it is a bit of a pain since most people don’t really understand the syntax within, but it is heavily relied upon for those doing translation work. Katello’s strange UI testing is another place that could probably benefit. Take a look in katello/engines/bastion_katello/README.md at master · Katello/katello · GitHub

To the question of relying on our Rails framework, should rake be handling these random scripting requirements that we have? If rake could reliably replace the Makefile that lives in the Katello locale directory without being hacky or strange, then perhaps rake is what we should lean more into. I don’t have the answer to that right now.