zachdaniel / git_ops

A tool for version and changelog management in Elixir via conventional commits.
MIT License
135 stars 24 forks source link

Version File Instead of Mix.exs #16

Open asummers opened 5 years ago

asummers commented 5 years ago

So inside a mix.exs file, you can read files from priv, and in order to play nicely with different tools that interact with version files, it might be nice to be able read from e.g. /priv/VERSION.txt or something along those lines. Would it be possible to add the ability to specify the location of the version? The @version tag would then @version File.read!("/priv/VERSION.txt") in that case.

One concern I have is that then :manage_mix_version? becomes weird, so it may need to be broken our to (e.g.) :manages_version? and :version_location with special behavior based on if that is mix.exs.

zachdaniel commented 5 years ago

I think a behaviour like the following would be the cleanest:

defmodule GitOps.VersionStorage do
  @callback read_version() :: {:ok, String.t()} | {:error, String.t()}
  @callback write_version() :: {:ok, String.t()} | {:error, String.t()}
end
asummers commented 5 years ago

I have no problem with that, but then I guess the question becomes - do we by always have the version in the source somewhere? Or would it bifurcate on @version being String.t() | module()? Can you even reference a module in mix.exs?

zachdaniel commented 5 years ago

You can reference a module anywhere, but I wouldn't do it there. We'd provide one called GitOps.VersionStorage.MixModuleAttribute, and one called GitOps.VersionStorage.File. They would put it in their application config which one they wanted to use, like

config :git_ops,
  version_storage: MixModuleAttribute

# OR

config :git_ops,
  version_storage: {MixModuleFile, [path: "priv/VERSION.txt"]}
asummers commented 5 years ago

Got it. That seems like it would do what we need.

simpers commented 2 years ago

Was there anything done on this? I have need of this too so might pick this one up

zachdaniel commented 2 years ago

Nothing explicitly was done for this, but I have just recently pushed an addition (but not released it yet, because its untested and I still want to try it out) that allows you to manage multiple "readme" files, which may also help with what you're needing. e.g you can say manage_readme_version: ["README.md", "foo.md", ...]

simpers commented 1 year ago

@zachdaniel is this README managing feature merged? If so I could take a stab at this. Currently I calculate my project's version using this function:

def version() do
  version =
    Path.join(__DIR__, "VERSION")
    |> File.read!()
    |> String.trim()

  git =
    System.cmd("git", ["rev-parse", "--short", "HEAD"])
    |> elem(0)
    |> String.trim()

  [version, git]
  |> Enum.reject(&(is_nil(&1) or &1 == ""))
  |> Enum.join("-")
end

If :git_ops could handle the VERSION file I would lift out the git part of this function and add it to my CI instead (by echo-ing the -<git short sha> part into the file before building the release) and just rely on git_ops for managing the main part of it.

I'd imagine that what I'd need to do would be to read the VERSION file as early as possible, so that the new version can be calculated before any other steps, and then write it back to disk when all steps have succeeded. Otherwise if any of the steps fail, and you rerun the command, it would keep bumping for each failure. Thoughts?

zachdaniel commented 1 year ago

It kind of is, but it looks for ~> in front of the version it replaces. I think we should add manage_version_file as its own feature/option. Should be able to copy the logic for managing the readmes but just look for a single file by name and replace the contents exactly. PRs on that front welcome 😄

simpers commented 1 year ago

I'll see if I can get one done this week! :)

I already started a little bit, but looking at the code so far I realise that maybe one would want to point out where to source the version explicitly, rather than have options such as :manage_mix_version? and :manage_version_file? at the same time. What would setting both mean?

Instead – with backwards compatibility – one could have :version_source that could default to nil, keeping the original behaviour, but for new projects it could default to :mix_module. And possible values could be: [:mix_module, :version_file] for now. Thoughts? 🤔