liquibase / liquibase-gradle-plugin

A Gradle plugin for Liquibase
Other
199 stars 59 forks source link

Documentation seems unclear on how activites are applied to commands. #81

Closed nsoft closed 1 year ago

nsoft commented 3 years ago

Appologies if I've missed some docs somewhere, but best I can tell the README in this repo is pretty much the extent of the documentation. If you'll help me understand, I'll try to submit a patch with clearer docs. Issues I'm having with what I've found there:

  1. The suggested configurations seem to require the application of properties on the command line that are specific to liquibase. If the example liquibase block is added 4 properties must be expressed on the command line regardless of whether or not the current task graph involves liquibase. (otherwise the build will error out with something like: > Cannot get property 'runList' on extra properties extension as it does not exist Three out of four of these are password/db/url and setting those in gradle.properties seems fine, but it seems like the docs say that including an activity in the runlist will run that activity.
  2. At first blush the name activity and it's association with parameters for commands appears to imply the running of liquibase commands based on the activity regardless of the tasks specified, but that sounds bonkers since it's unclear how the commands would be chosen and also since this is defined at config time which would be very un-gradle like... so...
  3. The next guess is that specifying an activity sets up a list of arguments ready and waiting in the event that a liquibase command is to be run but, there's no mapping evident from activity to particular commands so then the only possibility left seems to be that all activities apply to all commands being run. Although the docs imply an ordering based on the list (in a backhanded fashion by mentioning unspecified order when no list), the ability to specify an order is meaningless unless we know the precedence order (first wins, like ant properties? Last wins like setting variables?)

Basically the core problem here is it is very unclear how the args supplied are mapped to the gradle tasks that require them. If the activites contribute args in a cascading fashion the precedence order of the cascade should be clarified if there is a means to specify that an activity maps to a particular command it would be great to document that.

The docs could really use some worked examples including ./gradlew invocations, specifying different runlists and an exposition of the resulting liquibase command, especially including an example where there is conflict between the parameters specified among activities.

Finally as a side note, the docs seem incorrect where they say "Using this plugin with Java 9+ and XML based change sets will need to add JAXB" as I still get Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlElement even if I specify a .groovy file extension, and have no xml files.

As it stands I can make it work by adding these blocks in, and comment/uncomment them such that only one activity exists and one command is specified when I need to run a particular liquibase task, but that's far from ideal, and I suspect not at all what is intended, but I don't know how to predict the results of anything else (at least not without delving into the code which I don't really have time for, and folks really shouldn't need to do anyway).

stevesaliman commented 3 years ago

Thank you for offering to help fix the README file - that file is a continuous work in progress :smiley: I think your idea for an Examples section is a good one. We could describe common use cases, and show the liquibase block that would need to be used in that case.

For Item 1, the liquabase block in README is an example, not a suggestion. It shows what a configuration could look like, using build variables for things that a typical user would not want to have hard coded in the build script, such as connection information which changes from environment to environment. If users choose to use a project property for things like credentials, and they don't put them in gradle property files, then they will have to either use environment variables or command line parameters, which is pretty standard practice when dealing with credentials. I think the trick here is to make it more clear that the block is not meant to be something that is just pasted into users' build scripts, but a sample of what is possible. I try to explain some of these things in the section below the sample block.

Items 2 and 3 are trickier. I try to explain these as well in the section below the sample block, but there is obviously room for improvement.

There are three kinds of things that need to be understood to configure the plugn; Liquibase commands, Liquibase parameters, and Plugin activities.

Liquibase commands are the things you want Liquiabse to do, as described in the Liquibase Command Documentation. These commands are mapped to Gradle tasks by the plugin. So if Liquibase has an update command, the plugin creates a Gradle update task.

Liquibase parameters are the options that are passed to Liquibase before the command, and they configure how Liquibase will run. This is where we set the connect string, the credentials, the changelog file, etc. Any method called in an activity is assumed to be the name of a Liquibase supported parameter. It doesn't help that Liquibase calls them options on some pages, and parameters in others. I call them parameters, which is what Liquibase calls them on their page that describes the valid Liquibase parameters

Plugin activities are the toughest to understand, and most builds will only need one. The paragraph above the sample block in the README explains the use case for multiple activities. They are most useful when you would need to update two different kinds of databases in the same build. Basically, each activity defines a configuration, and the runList decides which activities actually get run.

When you run a gradle Liquibase task, that task will be run by each activity in the run list (which defaults to all activities), using the parameters defined for the activity. So if you had a liquibase block that looked like the sample block, and a run list that included the main and security activities, and you ran gradlew update, The plugin would ask Liquibase to run the update command, once with the parameters in the main activity, and once with the parameters in the security block.

For your situation, do you ever need to talk to more than one database at a time? If not (and most people don't), then you probably need only one activity. You can then use gradle properties to point to different databases, such as dev, test, prod, etc. From there, you only need to make sure the properties you need get set at runtime. You can set them in environment variables, pass them on the command line with -P options, or if you can forgive the shameless plug, you can use the Gradle Properties Plugin and have the values for each environment in its own file and simply provide a value for -PenvironmentName at build time.

As far as the JAXB issue, I'm not exactly sure what is going on there, I just followed the recommendation in #75

I hope this helps.

stevesaliman commented 1 year ago

Updated the README file to (hopefully) provide more clarity.