1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
|
(*---------------------------------------------------------------------------
Copyright (c) 2016 Daniel C. Bünzli. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)
let topkg_manual = "Topkg manual"
let version = "v1.1.1"
(* Help manuals *)
open Cmdliner
let see_also ~cmds =
let cmds = (Astring.String.concat ~sep:"(1), " ("topkg" :: cmds)) ^ "(1)" in
[ `S Manpage.s_see_also; `P cmds ]
let release =
("TOPKG-RELEASE", 7, "", version, topkg_manual),
[ `S Manpage.s_name;
`P "topkg-release - How to release a (topkg) package";
`S Manpage.s_description;
`P "The basic release script is the following. Each step is
refined and explained with more details below.";
`Pre "\
topkg browse issues # Review remaining outstanding issues
topkg status # Review the changes since last version
topkg log edit # Write the release notes
topkg log commit # Commit the release notes
topkg tag # Tag the distribution with a version
topkg distrib # Create the distribution archive
topkg publish # Publish it on the WWW with its documentation
topkg opam pkg # Create an opam package
topkg opam submit # Submit it to OCaml's opam repository";
`P "The last four steps can be performed via a single invocation
to topkg-bistro(1).";
`S "BASIC CHECKS";
`P "First have a look at the outstanding issues the package may have
by checking the issue tracker.";
`Pre "topkg browse issues";
`P "If the package's delegate supports issue tracker interaction
(see topkg-delegate(7)), you can consult them directly in the
terminal with:";
`Pre "topkg issue list";
`P "Basic checks are performed on the distribution archive when it is
created, but save time by catching errors early. Hence test that
your source repository lints and that it builds in the current build
environment and that the package tests pass.";
`Pre "\
topkg lint
topkg build # Check out the generated opam install file too
topkg test";
`S "WRITE THE RELEASE NOTES";
`P "Carefully write the release notes in the package's change log, these
are essential time savers for users of the package. It may help to
consult the list of changes that were committed since the last VCS
version tag with:";
`Pre "topkg status";
`P "You can then write the release notes and commit them to the VCS with:";
`Pre "\
topkg log edit
topkg log commit";
`P "The next step is simplified if the change log follows a certain
format, see topkg-log(1) for details.";
`P "The last two commands mentioned perform no magic, it is entirely up
to you to use them or not. The first one simply opens the change log
of the package in your \\$EDITOR and the second one commits it to
your VCS with a dull, canned, commit message.";
`S "VCS TAG THE RELEASE";
`P "Here again topkg provides a magic-less command that will simply
extract the latest version tag from the package's change log
and tag the VCS HEAD commit with it:";
`Pre "topkg tag";
`P "This will only work if the change log follows a certain format,
see tokpg-log(1) for details. You can check the extracted tag is
the one you wish before with:";
`Pre "topkg log -t";
`P "If you do not want to rely on topkg's broken extraction algorithms
just specify it on the command line:";
`Pre "topkg tag v1.0.1";
`P "And if you really think topkg does a bad job at this, simply
use your VCS directly to tag a release.";
`S "CREATE THE DISTRIBUTION ARCHIVE AND PUBLISH IT";
`P "Now that the release is tagged in your VCS, generate a distribution
archive for it in the build directory with:";
`Pre "topkg distrib";
`P "This uses the source tree of the HEAD commit for creating a
distribution in the build directory. The distribution version
string is the VCS tag description (e.g. git-describe(1)) of
the HEAD commit. Alternatively it can be specified on the command
line.";
`P "If everything went well you can now publish the distribution and
its documentation on the WWW. The exact actions that happen here
depend on the package's delegate, see topkg-delegate(7) for more
details.";
`Pre "topkg publish";
`P "The distribution is now public. It may already have been picked up
by other systems hence do not try to alter the archive and
republish it with a different bit-stream after that point (if
you are tempted to do this please consider taking a functional
programming course). At worst 410 the archive from
the WWW. But in most cases, if there is a problem with the
archive, simply leave it there and publish a new one with an
increased patch version number.";
`S "SUBMIT TO OCAML'S OPAM REPOSITORY";
`P "The following steps still need the distribution archive created in
the preceeding step to be in the build directory. If that's no
longer the case but nothing moved in your VCS, you can simply
invoke $(b,topkg distrib), it should produce a bit-wise identical
archive. If the VCS moved checkout the distribution commit to
regenerate the archive or provide, in the subsequent commands,
the archive manually via the $(b,--dist-file) option, see
topkg-opam(1) for details.";
`P "To add the package to OCaml's opam repository, start by creating an
opam package description in the build directory with:";
`Pre "topkg opam pkg";
`P "then simply submit it to the opam repository with:";
`Pre "topkg opam submit";
`P "The latter does nothing more than invoking opam-publish-submit(1) on
the package description generated earlier.";
`P "Congratulations. You are done. Ditch your computer.";
`S "TROUBLESHOOTING";
`P "Here are a few troubleshooting scenarios and possible resolution.";
`I ("Before publishing",
"Anything that happens before the $(b,topkg publish) step,
like a failing $(b,topkg distrib), is easy to resolve. Delete the
version tag of your VCS, a $(b,topkg tag -d) will do, add
some commits, adjust your release notes and start over.");
`I ("opam submission build failure",
"If the build failure is due to a missing constraint, follow the
instruction of the next item to correct the opam file. If the failure
is due to a defect in the distribution archive, call it a day and
start over with a patch version release that corrects the problem.
Do not try to reuse the version string of the failed release, other
systems may already have picked up the broken archive.");
`I ("opam repository maintainer and robot complaints",
"These pesky but loved maintainers and robots... If they
complain about certain aspects of your opam submission, you can either
try to correct it manually from the opam package description found
in the build directory and reinvoke $(b,topkg opam submit) or edit
the opam file of the source repository and regenerate the opam Package
description with $(b,topkg opam pkg) and the $(b,--pkg-opam)
option. Note that if the VCS moved meanwhile you may have to use
the various command line options of topkg-opam(1) to make sure
you point to the right package version and distribution archive.
In either case you should be aware that there will be a mismatch
between the opam file in the distribution archive and the one
you submitted to the opam repository. If this happens to be a
problem, start over with a new patch version release.");
`Blocks (see_also ~cmds:[]); ]
let delegate =
("TOPKG-DELEGATE", 7, "", version, topkg_manual),
[ `S Manpage.s_name;
`P "topkg-delegate - The topkg delegate";
`S Manpage.s_description;
`P "The delegate of a package is a program invoked by topkg to perform
actions that are difficult or not desirable to standardize within
topkg itself, namely:";
`I ("$(b,topkg publish)",
"Publish distribution archives and derived artefacts.");
`I ("$(b,topkg issue)", "Interact with the package's issue tracker.");
`P "A sample delegate is provided at the end of this man page.";
`S "DELEGATE LOOKUP PROCEDURE";
`P "The delegate used by a package is defined by the first match in the
following lookup procedure.";
`I ("1. Command line", "Specified with the $(b,--delegate) option on
the command line.");
`I ("2. Package description.", "Specified in the package description file,
see topkg's API documentation.");
`I ("3. Environment variable.", "Specified in the TOPKG_DELEGATE
environment variable.");
`I ("4. Homepage derived discovery.", "Consult the 'homepage' field of the
package's opam file, extract the second-level domain of the URI as
\\$NAME and uses the tool $(b,\\$NAME-topkg-delegate) iff it exists
in the executable search path. For example if the homepage is
http://www.example.org/mypackage, an existing
$(b,example-topkg-delegate) tool will be used.");
`I ("5. Transitory toy github fallback.", "If the previous step yields
$(b,github-topkg-delegate) but that it doesn't exist in the
executable search path. The $(b,toy-github-topkg-delegate) tool
distributed with topkg-care is used. This tool will disappear in
the future whenever a good github delegate emerges. Consult
$(b,toy-github-topkg-delegate --help) for more information.");
`S "DELEGATE PROTOCOL";
`P "The delegate is invoked by $(b,topkg) with a request in order to
finish its own execution. This means that the delegate takes over
$(b,topkg)'s standard channels and is in charge until the end of
execution (except on errors, see below).";
`P "The delegate always gets information as command line arguments with
file paths arguments given as absolute paths. The first argument is
always 'ipc' and is followed by a verbosity parameter:";
`P "my-topkg-delegate ipc $(i,VERB) $(i,ARG) ...";
`P "$(i,VERB) will be either `quiet', `error', `warning', `info' or
`debug' and the delegate must adjust its logging level appropriately.
The remaining arguments are the request, see below for requests
made by $(b,topkg).";
`P "The delegate must always exit with one of the following exit codes:";
`I ("0", "The request is successful."); `Noblank;
`I ("1", "The request is unsupported."); `Noblank;
`I ("2", "The request errored.");
`P "Exit 0 must be returned iff the request could be fulfilled according
to its semantics.";
`P "Exit 1 must be returned iff the request arguments cannot be
understood or if the request is not implemented by the delegate.
In this case the delegate must produce no output.";
`P "Exit 2 must be returned iff the request could not be fulfilled
according to its semantics. In this case it is the delegate's duty
to provide good error messages for diagnosis on standard output";
`P "In both non-zero exit codes, it is not the delegate's duty to
try to save request data. In these cases $(b,topkg) will take over
again in order to prevent user input data loss. This
occurs for example on issue creation, so that the issue
description the user may have input interactively is not
lost but \"saved\" to standard output.";
`S "PUBLISH DELEGATION";
`P "Publish delegation requests have the form:";
`P "publish $(i,ACTION) $(i,ARG)...";
`P "The following actions are currently defined.";
`I ("publish distrib $(i,DISTRIB_URI) $(i,NAME) $(i,VERSION)
$(i,MSG) $(i,ARCHIVE)",
"Publish the distribution archive file $(i,ARCHIVE) for the package
named $(i,NAME) at version $(i,VERSION) with publication
message $(i,MSG). See topkg API's documentation
for information about the value of $(i,DISTRIB_URI).");
`I ("publish doc $(i,DOC_URI) $(i,NAME) $(i,VERSION) $(i,MSG) $(i,DOCDIR)",
"Publish the documentation directory $(i,DOCDIR) for the package
named $(i,NAME) at version $(i,VERSION) with publication message
$(i,MSG). $(i,DOC_URI) has the value of the doc field of the
package's opam file.");
`I ("publish alt $(i,DISTRIB_URI) $(i,KIND) $(i,NAME) $(i,VERSION)
$(i,MSG) $(i,ARCHIVE)",
"Alternative publication artefact named $(i,KIND). The semantics
of the action is left to the delegate. The request arguments
are the same as those of the distrib action.");
`S "ISSUE DELEGATION";
`P "Issue delegation requests have the form:";
`P "issue $(i,ACTION) $(i,ISSUES_URI) $(i,ARG) ...";
`P "with $(i,ISSUES_URI) the value of the bug-reports field of the
package's opam file or \"\" if there is no such field.";
`P "The following actions are currently defined.";
`I ("issue list $(i,ISSUES_URI)",
"List open issues on standard output. Each issue should be on its
own line with the format '$(i,ID) $(i,TITLE)'.");
`I ("issue show $(i,ISSUES_URI) $(i,ID)",
"Show details about issue $(i,ID) on standard output.");
`I ("issue open $(i,ISSUES_URI) $(i,TITLE) $(i,MSG)",
"Create an issue with title $(i,TITLE) and description $(i,MSG)
(may be an empty string). If the request is successful the
delegate should communicate the resulting issue identifier in some
way on standard output.");
`I ("issue close $(i,ID) $(i,MSG)",
"Close issue $(i,ID) with closing message $(i,MSG).");
`S "SAMPLE UNSUPPORTIVE DELEGATE";
`P "This delegate script can be used as a blueprint. All requests
are simply unsupported.";
`Pre "\
#!/usr/bin/env ocaml
#use \"topfind\"
#require \"bos.setup\"
open Bos_setup
let unsupported = Ok 1
let publish = function
| \"distrib\" :: uri :: name :: version :: msg :: archive :: _ ->
unsupported
| \"doc\" :: uri :: name :: version :: msg :: docdir :: _ ->
unsupported
| \"alt\" :: kind :: uri :: name :: version :: msg :: archive :: _ ->
unsupported
| args ->
unsupported
let issue = function
| \"list\" :: uri :: _ -> unsupported
| \"show\" :: uri :: id :: _ -> unsupported
| \"open\" :: uri :: title :: descr :: _ -> unsupported
| \"close\" :: uri :: id :: msg :: _ -> unsupported
| args -> unsupported
let request = function
| \"publish\" :: args -> publish args
| \"issue\" :: args -> issue args
| args -> unsupported
let main () =
let doc = \"the unsupportive delegate\" in
begin match OS.Arg.(parse ~doc ~pos:string ()) with
| \"ipc\" :: verbosity :: req ->
Logs.level_of_string verbosity
>>= fun level -> Logs.set_level level; request req
| \"ipc\" :: [] ->
R.error_msg \"malformed delegate request, verbosity is missing\"
| args ->
R.error_msgf \"unknown arguments: %s\" (String.concat ~sep:\" \" args)
end
|> Logs.on_error_msg ~use:(fun () -> 2)
let () = exit (main ())
";
`Blocks (see_also ~cmds:["topkg-issue"; "topkg-publish"]); ]
let troubleshoot =
("TOPKG-TROUBLESHOOT", 7, "", version, topkg_manual),
[ `S Manpage.s_name;
`P "topkg-troubleshoot - A few troubleshooting tips";
`S Manpage.s_description;
`P "If you get into trouble try the following to get a better undersanding
of what is happening.";
`S "ASK FOR MORE LOGGING";
`P "Invoke $(b,topkg) with $(b,-v), $(b,-v -v), or use the
TOPKG_VERBOSITY environment variable; see the $(b,--verbosity)
option.";
`P "Messages comming from the $(b,topkg) tool are prefixed
by 'topkg:' while those comming from the package description are
prefixed by its base name, usually 'pkg.ml:'.";
`S "DEBUG THE GENERATED OPAM INSTALL FILE";
`P "To debug the generated opam install file according to the build
configuration you don't need to build the package. Use the
$(b,--dry-run) (or $(b,-d)) option and add a little bit of logging to
output the build configuration that was determined:";
`Pre "pkg/pkg.ml build -d -v [OPTION]...";`Noblank;
`Pre "topkg build -d -v [OPTION]... # mostly equivalent";
`S "DEBUG DEV PACKAGE INSTALLS";
`P "If you need more information about what happens when dev packages
are installed (VCS pins or VCS packages) in opam, for example the
actual watermark values, invoke opam as follows:";
`P "TOPKG_VERBOSITY=debug opam upgrade mypkg -v";
`S "RELEASE PROCESS TROUBLES";
`P "See the TROUBLESHOOTING section of topkg-release(7).";
`Blocks (see_also ~cmds:[]) ]
(* Help command *)
let pages =
[ "release", release;
"delegate", delegate;
"troubleshoot", troubleshoot; ]
let help man_format topic commands = match topic with
| None -> `Help (man_format, None)
| Some topic ->
let topics = "topics" :: commands @ (List.map fst pages) in
let topics = List.sort compare topics in
let conv =
Cmdliner.Arg.(conv_parser (enum (List.rev_map (fun s -> (s, s)) topics)))
in
match conv topic with
| Error (`Msg e) -> `Error (false, e)
| Ok t when List.mem t commands -> `Help (man_format, Some t)
| Ok t when t = "topics" ->
Fmt.pr "@[<v>%a@]@." Fmt.(list string) topics;
`Ok 0
| Ok t ->
let man = try List.assoc t pages with Not_found -> assert false in
Fmt.pr "%a" (Cmdliner.Manpage.print man_format) man;
`Ok 0
(* Command line interface *)
open Cmdliner
let topic =
let doc = "The topic to get help on, `topics' lists the topic." in
Arg.(value & pos 0 (some string) None & info [] ~docv:"TOPIC" ~doc)
let doc = "Show help about topkg"
let sdocs = Manpage.s_common_options
let exits = Cli.exits
let man_xrefs = [`Main]
let man =
[ `S Manpage.s_description;
`P "The $(tname) command shows help about $(mname).";
`P "Use `topics' as $(i,TOPIC) to get a list of topics." ]
let cmd =
Cmd.v (Cmd.info "help" ~doc ~exits ~man ~man_xrefs) @@
Term.(ret (const help $ Arg.man_format $ topic $ Term.choice_names))
|