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

@nifinet: https://x.com/nifinet/status/2078851409068654639

X AI KOLs Timeline

A guide on building a self-improving outbound system using Codex, where the AI agent reads past outcomes, edits scoring and playbook files, runs tests, and proposes pull requests for human approval. It emphasizes treating go-to-market logic as versioned code that improves from market feedback.

@shmidtqq: https://x.com/shmidtqq/status/2068704187492221405

X AI KOLs Timeline

An in-depth guide to loop engineering for AI coding agents, explaining how to build automated loops that repeatedly prompt agents, verify results, and avoid runaway costs, illustrated with a case study of one engineer shipping 259 PRs in a month.