A tiny command-line utility that helps to answer a simple question:
What is the latest stable version for a project?
... and, optionally, download/install it.
Supported project locations:
- GitHub
- GitLab
- BitBucket
- Mercurial
- SourceForge
GitHub has an API endpoint here. But if you're here, then you know how it sucks:
A release would show up in this API response only if it was filed formally using the GitHub release interface. Sometimes project authors use formal releases, and next thing you know, for the next release they won't. There is no consistency in human beings.
OK, you think you could use another API endpoint to list tags. Tags usually represent a release, however, the API does not sort them chronologically. Moreover, you might get something like "latest-stable" for a tag name's value.
In general, quite many project authors complicate things further by:
- Creating a formal release that is clearly a Release Candidate (
rc
in tag), but forgetting to mark it as a pre-release - Putting extraneous text in release tag e.g.
release-1.2.3
orname-1.2.3-2019
anything fancy like that - Putting or not putting the
v
prefix inside release tags. Today yes, tomorrow not. I'm not consistent about it myself :) - Switching from one version format to another, e.g.
v20150121
tov2.0.1
To deal with all this mess and simply get well-formatted, last stable version (or download URL!)
on the command line, you can use lastversion
.
Its primary use is for build systems - whenever you want to watch specific repositories for released versions to build packages automatically. Or otherwise require getting the latest version in your automation scripts.
lastversion
does a little bit of AI to detect if releasers mistakenly filed a beta version as a
stable release.
It uses both of the API endpoints and incorporates logic for cleaning up human inconsistency from
version information.
lastversion apache/incubator-pagespeed-ngx
#> 1.13.35.2
lastversion apache/incubator-pagespeed-ngx -d
#> downloaded incubator-pagespeed-ngx-v1.13.35.2-stable.tar.gz
lastversion apache/incubator-pagespeed-ngx -d pagespeed.tar.gz
#> downloads with chosen filename
sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm
sudo yum install lastversion
Installing with pip
is easiest:
pip install lastversion
Typically, you would just pass a repository URL (or repo owner/name to it) as the only argument, e.g.:
lastversion https://github.com/gperftools/gperftools
Equivalently accepted invocation with same output is:
lastversion gperftools/gperftools
If you're lazy to even copy paste a project's URL, you can just type its name as argument, which will use repository search API (slower). Helps to answer what is the latest Linux version:
lastversion linux
Or wondering what is the latest version of WordPress? :
lastversion wordpress
A special value of self
for the main argument, will lookup the last release of lastversion
itself.
For more options to control output or behavior, see --help
output:
usage: lastversion [-h] [--pre] [--verbose] [-d [FILENAME]]
[--format {version,assets,source,json,tag}] [--assets]
[--source] [-gt VER] [-b MAJOR] [--filter REGEX] [-su] [-y]
[--version]
[action] <repo or URL>
Get the latest release from GitHub/GitLab/BitBucket.
positional arguments:
action Special action to run, e.g. download, install, test
<repo or URL> GitHub/GitLab/BitBucket repository in format
owner/name or any URL that belongs to it
optional arguments:
-h, --help show this help message and exit
--pre Include pre-releases in potential versions
--verbose Will give you idea of what is happening under the hood
-d [FILENAME], --download [FILENAME]
Download with custom filename
--format {version,assets,source,json,tag}
Output format
--assets Returns assets download URLs for last release
--source Returns only source URL for last release
-gt VER, --newer-than VER
Output only if last version is newer than given
version
-b MAJOR, --major MAJOR
Only consider releases of a specific major version,
e.g. 2.1.x
--filter REGEX Filters --assets result by a regular expression
-su, --shorter-urls A tiny bit shorter URLs produced
-y, --assumeyes Automatically answer yes for all questions
--version show program's version number and exit
The --format
will affect what kind of information from the last release and in which format will
be displayed, e.g.:
version
is the default. Simply outputs well-formatted version number of the latest releaseassets
will output a newline-separated list of assets URLs (if any), otherwise link to sources archivesource
will output link to source archive, no matter if the release has some assets addedjson
can be used by external Python modules or for debugging, it is dict/JSON output of an API call that satisfied last version checkstag
will emit just the latest release's tag name, which useful if you're constructing download URL yourself or need the tag name otherwise
An asset is a downloadable file that typically represents an executable, or otherwise "ready to launch" project. It's what you see filed under formal releases, and is usually a compiled (for specific platform), program.
Source files, are either tarballs or zipballs of sources for the source code of release.
You can display either assets or source URLs of the latest release, by passing the corresponding
--format flag
, e.g. --format source
You also simply pass --source
instead of --format source
, and --assets
instead of
--format assets
, as in:
lastversion --assets mautic/mautic
#> https://github.com/mautic/mautic/archive/2.15.1/mautic-2.15.1.tar.gz
By default, lastversion
filters output of --assets
to be OS specific. Who needs .exe
on Linux?
To override this behavior, you can use --filter
, which has a regular expression as its argument.
To disable OS filtering, use --filter .
, this will match everything.
You can naturally use --filter
in place where you would use grep
, e.g.
lastversion --assets --filter win REPO
You can also use lastversion
to download assets/sources for the latest release.
Download the most recent Mautic source release:
lastversion mautic/mautic --download
Customize downloaded filename (works only for sources, which is the default):
lastversion mautic/mautic --download mautic.tar.gz
Or you can just have lastversion
output sources/assets URLs and have those downloaded by
something else:
wget $(lastversion --assets mautic/mautic)
This will download all assets of the newest stable Mautic, which are 2 zip files.
How this works: lastversion
outputs all asset URLs, each on a new line, and wget
is smart
enough to download each URL. Magic :)
For releases which have no assets added, it will download source archive.
To always download source, use --source
instead:
wget $(lastversion --source mautic/mautic)
We consider latest release is the one which is stable / not marked as beta.
If you think otherwise, then pass --pre
switch and if the latest version of repository is a
pre-release, then you'll get its version instead:
lastversion --pre mautic/mautic
#> 2.15.2b0
For some projects, there may be several stable releases available simultaneously, in different
branches. An obvious example is PHP. You can use --major
flag to specify the major release
version to match with, to help you find latest stable release of a branch, like so:
lastversion php/php-src --major 7.2
This will give you current stable version of PHP 7.2.x, e.g. 7.2.28
.
lastversion https://nginx.org --major stable #> 1.16.1
lastversion https://nginx.org --major mainline #> 1.17.9
Behind the scenes, this checks with hg.nginx.org
which is a Mercurial web repo.
Those are supported as well, e.g.
lastversion https://hg.example.com/project/
Mercurial repos are rather rare these days, but support has been added primarily for NGINX.
If a project provides .rpm
assets and your system has yum
or dnf
, you can install the project's
RPM directly, like so:
sudo lastversion install mailspring
This finds MailSpring, gets its latest release info,
filters assets for .rpm
and passes it to yum
/ dnf
.
You can even set up an auto-updater cron job which will ensure you are on the latest version of a package, like so:
@daily /usr/bin/lastversion install mailspring -y 2>/dev/null
If the Mailspring GitHub repo posts a release with newer .rpm
, then it will be automatically
installed, making sure you are running the latest and greated Mailspring version.
You'll even get an email alert after update (standard cron feature).
Needless to say, more often than not such RPM packages have no idea about all potentially missing
dependencies. Thus, only use lastversion install ...
if the software is missing from the base
yum
repositories.
The test
command can be used for troubleshooting or simply well formatting a string with version:
lastversion test 'blah-1.2.3-devel' # > 1.2.3.dev0
lastversion test '1.2.x' # > False (no clear version)
lastversion test '1.2.3-rc1' # > 1.2.3rc1
When you're building some upstream package, and you did this before, there is a known "last build" version. Automatic builds become easy with:
CURRENTLY_BUILT_VER=1.2.3 # stored somewhere, e.g. spec file in my case
LASTVER=$(lastversion repo/owner -gt ${CURRENTLY_BUILT_VER})
if [[ $? -eq 0 ]]; then
# LASTVER is newer, update and trigger build
# ....
fi
Here, the -gt
is actually a switch passed to lastversion
, which acts in a similar fashion to
-gt
comparison in bash.
There is more to it, if you want to make this reliable.
See my ranting on
RPM auto-builds with lastversion
Exit status codes are the usual means of communicating a command's execution success or failure.
So lastversion
follows this: successful command returns 0
while anything else is an error of
some kind:
Exit status code 1
is returned for cases like no release tag existing for repository at all, or
repository does not exist.
Exit status code 2
is returned for -gt
version comparison negative lookup.
Exit status code 3
is returned when filtering assets of last release yields empty URL set
(no match)
Getting latest version is heavy on the API, because GitHub does not allow to fetch tags in chronological order, and some repositories switch from one version format to another, so we can't just consider highest version to be latest. We have to fetch every tag's commit date, and see if it's actually more recent. Thus it's slower with larger repositories, which have potentially a lot of tags.
Thus, lastversion
makes use of caching API response to be fast and light on GitHub API,
It does conditional ETag validation, which, as per GitHub API will not count towards rate limit.
The cache is stored in ~/.cache/lastversion
on Linux systems.
It is much recommended to setup your GitHub API token in
~/.bashrc
like this, to increase your rate limit:
export GITHUB_API_TOKEN=xxxxxxxxxxxxxxx
For GitLab, you can use a Personal Access Token:
export GITLAB_PA_TOKEN=xxxxxxxxxxxxxxx
Then run source ~/.bashrc
. After this, lastversion
will use it to get larger API calls allowance
from GitHub.
Invoke lastversion.latest
function get version information for a repo.
from lastversion import lastversion
from packaging import version
latest_mautic_version = lastversion.latest("mautic/mautic", output_format='version', pre_ok=True)
print('Latest Mautic version: {}'.format(str(latest_mautic_version))
if latest_mautic_version >= version.parse('1.8.1')
print('It is newer')
With output_format='version'
(the default), the function returns a
Version object, or
False
. So you can do things like above, namely version comparison, checking dev status, etc.
The lastversion.latest
function accepts 3 arguments
repo
, in format of<owner>/<name>
, or any URL under this repository, e.g.https://github.com/dvershinin/lastversion/issues
format
, which accepts same values as when you runlastversion
interactivelypre_ok
, boolean for whether to include pre-releases as potential versions
LATEST_KERNEL=$(lastversion linux -gt $(uname -r | cut -d '-' -f 1))
if [[ $? -eq 0 ]]; then
echo "I better update my kernel now, because ${LATEST_KERNEL} is there"
else
echo "My kernel is latest and greatest."
fi