Prolog Projects Tips

Lobsters Hottest Tools

Summary

Tips for automating Prolog project releases using a Prolog script to increment versions, commit, tag, and register new pack versions with SWI-Prolog's pack system.

<p><a href="https://lobste.rs/s/fbietj/prolog_projects_tips">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 05/16/26, 07:09 AM

# Prolog Projects Tips Source: [https://occasionallycogent.com/prolog_project_helpers/index.html](https://occasionallycogent.com/prolog_project_helpers/index.html) This one is a little more involved, to the point it’s written in Prolog instead of bash\. I run it to generate a new release for the library\. The first part is pretty simple:`update\_pack\_version/2`parses the version number in the`pack\.pl`file, increments it based on whether the new version is major, minor, or patch \(using`increment\_version/3`\), then writes the new number back\.`git\_commit\_and\_tag/1`does what it says, plus pushes it up`origin`\. The final step, implemented by`register\_new\_pack/1`is a bit more mysterious\. It fixes the somewhat annoying issue that after pushing a new version, I have to manually install that new version by URL to get the SWI\-Prolog server to see the new version & register it \(so normal users can just run`pack\_upgrade\(Whatever\)`\)\. Previously I would just manually`pack\_remove/1`the package in a top\-level and run`pack\_install/2`, explicitly passing the URL of the bundle to install, but of course, we can automate that\! Knowing that’s why we’re doing this hopefully makes`register\_new\_pack/1`less mysterious\. The`download\_pattern\_format\_string/2`helper predicate turns the pattern in`pack\.pl`into a format string \(e\.g\.`'https://example\.com/release/\*\.zip'`to`"https://example\.com/release/~w\.zip"`\) so we can use`format/3`to download the new archive for the new version\. Unfortunately there’s some special\-casing for repos that are still on Github, as for reasons I do not recall the actual download URL and the one specified in the pack file are different, hence the first clause of the predicate\. Finally,`main/1`checks that a valid release type was given, prompts for confirmation, then does the thing\. I added the prompt after I accidentally ran the script from history when trying to re\-run tests and had to force\-push one too many times 😅 Listing 3:scripts/make\_release\.pl ``` #!/usr/bin/env swipl :- module(make_release, []). :- use_module(library(readutil), [read_file_to_terms/3, read_line_to_string/2]). :- initialization(main, main). increment_version(major, [Major0, _Minor, _Patch], [Major1, 0, 0]) :- !, succ(Major0, Major1). increment_version(minor, [Major, Minor0, _Patch], [Major, Minor1, 0]) :- !, succ(Minor0, Minor1). increment_version(patch, [Major, Minor, Patch0], [Major, Minor, Patch1]) :- !, succ(Patch0, Patch1). update_pack_version(ReleaseType, NewVersion) :- read_file_to_terms('pack.pl', PackTerms, []), memberchk(version(OldVersion), PackTerms), atomic_list_concat([MajorS, MinorS, PatchS], '.', OldVersion), maplist(atom_number, [MajorS, MinorS, PatchS], VersionNums0), increment_version(ReleaseType, VersionNums0, VersionNums1), atomic_list_concat(VersionNums1, '.', NewVersion), once(select(version(OldVersion), PackTerms, version(NewVersion), NewPackTerms)), setup_call_cleanup(open('pack.pl', write, S, []), forall(member(T, NewPackTerms), write_term(S, T, [fullstop(true), nl(true), quoted(true), spacing(next_argument) ])), close(S)). git_commit_and_tag(NewVersion) :- shell('git add pack.pl'), shell('git commit -m "Bump version"'), format(atom(TagCmd), "git tag v~w", [NewVersion]), shell(TagCmd), format(atom(PushCmd), 'git push origin master v~w', [NewVersion]), shell(PushCmd). download_pattern_format_string(DownloadURLPat, FormatString) :- string_concat("https://github.com", _, DownloadURLPat), !, string_concat(Prefix, "releases/*.zip", DownloadURLPat), string_concat(Prefix, "archive/refs/tags/v~w.zip", FormatString). download_pattern_format_string(DownloadURLPat, FormatString) :- file_name_extension(Base0, Ext, DownloadURLPat), string_concat(Base, "*", Base0), format(string(FormatString), "~s~s.~s", [Base, "v~w", Ext]). register_new_pack(NewVersion) :- read_file_to_terms('pack.pl', PackTerms, []), memberchk(name(ProjectName), PackTerms), memberchk(download(DownloadURLPattern), PackTerms), download_pattern_format_string(DownloadURLPattern, URLFormat), ( pack_remove(ProjectName) -> true ; true ), format(atom(Url), URLFormat, [NewVersion]), pack_install(ProjectName, [url(Url), interactive(false)]). main(Args) :- ( Args = [ReleaseType], increment_version(ReleaseType, [0, 0, 0], _) -> true ; ( format(user_error, "Usage: make_release.pl [major|minor|patch]~n", []), halt(1) ) ), ( stream_property(user_input, tty(true)) -> format("Make new release? [y/n]: ", []), read_line_to_string(user_input, Input), Input == "y" ; true ), update_pack_version(ReleaseType, NewVersion), format("Bumping to ~w~n", [NewVersion]), git_commit_and_tag(NewVersion), register_new_pack(NewVersion). ``` May these be of some use to you in using & learning Prolog\! Some day soon I will try to stitch these together, along with some other helpful libraries, tools, and templates I habitually use, into a nice unified tool that will help bring Prolog to more of a mainstream developer audience\. Until then, enjoy and use wisely\!

Similar Articles

Prolog Coding Horror

Hacker News Top

A guide to common pitfalls in Prolog programming, emphasizing the use of pure and declarative constructs over impure ones like cuts, global state, and low-level I/O.

My Gripes with Prolog

Hillel Wayne — Computer Things

A blog post by Hillel Wayne detailing his frustrations with the Prolog programming language, including issues with strings, lack of functions, limited data types, and cuts.

Prolog Basics Explained with Pokémon

Lobsters Hottest

An introduction to Prolog programming using Pokémon type matchups as a motivating example, demonstrating how logic programming can elegantly model relational data.

Stamp It! All Programs Must Report Their Version

Michael Stapelberg

The article advocates for mandatory version stamping in all software programs to improve incident response, using the i3 window manager's version reporting system as a case study, and covers implementation details with Go and NixOS.