.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Pinto::Manual::Tutorial 3pm" .TH Pinto::Manual::Tutorial 3pm "2022-10-16" "perl v5.34.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Pinto::Manual::Tutorial \- A narrative introduction to Pinto .SH "VERSION" .IX Header "VERSION" version 0.14 .SH "INTRODUCTION" .IX Header "INTRODUCTION" This tutorial walks you through some of the typical use cases for a Pinto repository. Along the way, it demonstrates most of the pinto commands. You are encouraged to try the commands as you read along. If you would prefer to get a more condensed summary of features and commands, please read the Pinto::Manual::QuickStart. For detailed instructions on installing the software read Pinto::Manual::Installing. .SH "BASIC OPERATIONS" .IX Header "BASIC OPERATIONS" .SS "Creating a repository" .IX Subsection "Creating a repository" The first step in using Pinto is to create a repository, using the init command like this: .PP .Vb 1 \& pinto \-\-root ~/repo init .Ve .PP This will create a new repository in the \fI~/repo\fR directory. If that directory does not exist, it will be created for you. If it already does exist, then it must be empty. .PP The \f(CW\*(C`\-\-root\*(C'\fR (or \f(CW\*(C`\-r\*(C'\fR) option specifies where the repository is. This argument is required for every pinto command. But if you get tired of typing it, you can set the \f(CW\*(C`PINTO_REPOSITORY_ROOT\*(C'\fR environment variable to point to your repository instead. .PP The repository is created with a stack called \*(L"master\*(R" which is also marked as the default stack. We'll talk more about stacks and default stack later. .SS "Inspecting the repository" .IX Subsection "Inspecting the repository" Now that you have a repository, let's look inside it. To see the contents of a repository, use the list command: .PP .Vb 1 \& pinto \-\-root ~/repo list .Ve .PP You will use the list command quite often. But at this point, the listing will be empty because there is nothing in the repository. So let's go ahead and add something... .SS "Adding dependencies" .IX Subsection "Adding dependencies" Suppose we are working on an application called My-App that contains a package called \f(CW\*(C`My::App\*(C'\fR. The application also depends on the \s-1URI\s0 package. Using the pull command, you can bring the \s-1URI\s0 package into your repository: .PP .Vb 1 \& pinto \-\-root ~/repo pull URI .Ve .PP You will be prompted to enter a log message that describes why this change is happening. The message template will include a semi-informative generated message. Feel free to edit this message as you see fit. Save the file and close your editor when you are done. .PP Now, you should have \s-1URI\s0 in your local repository. So lets look and see what we really got. Once again, you use the list command to see inside the repository: .PP .Vb 1 \& pinto \-\-root ~/repo list .Ve .PP This time, the listing will look something like this: .PP .Vb 4 \& rf URI 1.60 GAAS/URI\-1.60.tar.gz \& rf URI::Escape 3.31 GAAS/URI\-1.60.tar.gz \& rf URI::Heuristic 4.20 GAAS/URI\-1.60.tar.gz \& ... .Ve .PP You can see that the \s-1URI\s0 package has been added to the repository, as well as all the prerequisites for \s-1URI,\s0 and all of their prerequisites, and so on. .SS "Adding your own distributions" .IX Subsection "Adding your own distributions" Now suppose that you've finished work on My-App and your ready to release the first version. Using your preferred build tool (ExtUtils::MakeMaker, Module::Build, Module::Install etc.) you package a release as \fIMy\- App\-1.0.tar.gz\fR. Now put the distribution into the repository with the add command: .PP .Vb 1 \& pinto \-\-root ~/repo add path/to/My\-App\-1.0.tar.gz .Ve .PP When you list the repository contents now, it will include the \f(CW\*(C`My::App\*(C'\fR package and show you as the author of the distribution: .PP .Vb 5 \& rl My::App 1.0 JEFF/My\-App\-1.0.tar.gz \& rf URI 1.60 GAAS/URI\-1.60.tar.gz \& rf URI::Escape 3.31 GAAS/URI\-1.60.tar.gz \& rf URI::Heuristic 4.20 GAAS/URI\-1.60.tar.gz \& ... .Ve .SS "Installing packages" .IX Subsection "Installing packages" Now the repository contains both your application and all of its prerequisites, so you can install it into your environment using the install command: .PP .Vb 1 \& pinto \-\-root ~/repo install My::App .Ve .PP When \f(CW\*(C`My::App\*(C'\fR is installed, it will only use the prerequisites that are in your repository. Even if a newer version of \s-1URI\s0 is released to the \s-1CPAN\s0 in the future, \f(CW\*(C`My::App\*(C'\fR will always be built with the same versions of the same prerequisites that you developed and tested against. This ensures your application builds will be stable and predictable. .PP On the surface, a Pinto repository looks like an ordinary \s-1CPAN,\s0 so you can also install packages from it using cpanm directly. All you have to do is point them at the \s-1URI\s0 of your repository (under the hood, this is all the install command is really doing anyway). For example: .PP .Vb 1 \& cpanm \-\-mirror file:///home/jeff/repo \-\-mirror\-only My::App .Ve .PP The \f(CW\*(C`\-\-mirror\-only\*(C'\fR flag is important because it tells cpanm to \fBnot\fR use the \f(CW\*(C`cpanmetadb\*(C'\fR to resolve packages. Instead you only want to use the index from \fByour\fR repository. .PP You can do the same thing with cpan and cpanp as well. See their documentation for information on how to set the \s-1URI\s0 of the repository. .SS "Upgrading a dependency" .IX Subsection "Upgrading a dependency" Suppose that several weeks have passed since you first released My-App and now \&\s-1URI\s0 version 1.62 is available on the \s-1CPAN.\s0 It has some bug critical fixes that you'd like to get. Again, we can bring that into the repository using the pull command. But since your repository already contains a version of \s-1URI,\s0 you must indicate that you want a *newer* one by specifying the minimum version that you want: .PP .Vb 1 \& pinto \-\-root ~/repo pull URI~1.62 .Ve .PP If you look at the listing again, this time you'll see the newer version of \&\s-1URI\s0 (and possibly other packages as well): .PP .Vb 5 \& rl My::App 1.0 JEFF/My\-App\-1.0.tar.gz \& rf URI 1.62 GAAS/URI\-1.62.tar.gz \& rf URI::Escape 3.38 GAAS/URI\-1.62.tar.gz \& rf URI::Heuristic 4.20 GAAS/URI\-1.62.tar.gz \& ... .Ve .PP If the new version of \s-1URI\s0 requires any new prerequisites, those will be in the repository too. Now when you install \f(CW\*(C`My::App\*(C'\fR, you'll get version 1.62 of \&\s-1URI.\s0 .SH "WORKING WITH STACKS" .IX Header "WORKING WITH STACKS" So far in this tutorial, we've treated the repository as a singular resource. For example, when we upgraded \s-1URI\s0 in the last section, it impacted every person and every application that might have been using the repository. But this kind of broad impact is undesirable. You would prefer to make those kinds of changes in isolation and test them before forcing everyone else to upgrade. This is what stacks are designed for. .SS "What is a stack" .IX Subsection "What is a stack" All CPAN-like repositories have an index which maps the latest version of each package to the archive that contains it. Usually, there is only one such index per repository. But with Pinto, there can be many indexes. Each of these indexes is called a \*(L"stack\*(R". This allows you to create different stacks of dependencies within a single repository. So you could have a \f(CW\*(C`development\*(C'\fR stack and a \f(CW\*(C`production\*(C'\fR stack. Whenever you add a distribution or upgrade a prerequisite, it only affects one stack. .SS "The default stack" .IX Subsection "The default stack" Before getting into the gory details, you first need to know about the default stack. For most operations, the name of the stack is an optional parameter. So if you do not specify a stack explicitly, then the operation is applied to whichever stack is marked as the default. .PP In any repository, there is never more than one default stack. When we created this repository, the \f(CW\*(C`master\*(C'\fR stack was marked as the default. You can also change the default stack or change the name of a stack, but we won't go into that here. See the default command to learn more about that. .PP Just remember that \f(CW\*(C`master\*(C'\fR is the name of the stack that was created when the repository was first initialized. .SS "Creating a stack" .IX Subsection "Creating a stack" Suppose your repository contains version 1.60 of \s-1URI,\s0 but version 1.62 has been released to the \s-1CPAN,\s0 just like in the earlier section. You want to try upgrading, but this time you're going to do it on a separate stack. .PP Thus far, everything you've added or pulled into the repository has gone onto the \f(CW\*(C`master\*(C'\fR stack. You could create an entirely new stack, but the \&\f(CW\*(C`master\*(C'\fR stack already has the prerequisites for My-App, so we're just going to make a clone using the copy command: .PP .Vb 1 \& pinto \-\-root ~/repo copy master uri_upgrade .Ve .PP This creates a new stack called \f(CW\*(C`uri_upgrade\*(C'\fR. If you want to see the contents of that stack, just use the list command with the \f(CW\*(C`\-\-stack\*(C'\fR option: .PP .Vb 1 \& pinto \-\-root ~/repo list \-\-stack uri_upgrade .Ve .PP The listing should be identical to the \f(CW\*(C`master\*(C'\fR stack: .PP .Vb 3 \& rl My::App 1.0 JEFF/My\-App\-1.0.tar.gz \& rf URI 1.60 GAAS/URI\-1.60.tar.gz \& ... .Ve .SS "Upgrading a stack" .IX Subsection "Upgrading a stack" Now that you've got a separate stack, you can try upgrading \s-1URI.\s0 Just as before, you'll use the pull command. But this time, you'll tell Pinto that you want the packages to be pulled onto the \&\f(CW\*(C`uri_upgrade\*(C'\fR stack: .PP .Vb 1 \& pinto \-\-root ~/repo pull \-\-stack uri_upgrade URI~1.62 .Ve .PP Now lets compare the \f(CW\*(C`master\*(C'\fR and \f(CW\*(C`uri_upgrade\*(C'\fR stacks using the diff command: .PP .Vb 1 \& pinto \-\-root ~/repo diff master uri_upgrade \& \& +rf URI 1.62 GAAS/URI\-1.62.tar.gz \& +rf URI::Escape 3.31 GAAS/URI\-1.62.tar.gz \& +rf URI::Heuristic 4.20 GAAS/URI\-1.62.tar.gz \& ... \& \-rf URI 1.60 GAAS/URI\-1.60.tar.gz \& \-rf URI::Escape 3.31 GAAS/URI\-1.60.tar.gz \& \-rf URI::Heuristic 4.20 GAAS/URI\-1.60.tar.gz .Ve .PP The output is similar to the \fBdiff\fR\|(1) command. Records starting with a \*(L"+\*(R" were added and those starting with a \*(L"\-\*(R" have been removed. .SS "Installing from a stack" .IX Subsection "Installing from a stack" With \s-1URI\s0 upgraded on the \f(CW\*(C`uri_upgrade\*(C'\fR stack, you can now try building and testing our application. All you have to do is run the install command and point to the right stack: .PP .Vb 1 \& pinto \-\-root ~/repo install \-\-stack uri_upgrade My::App .Ve .PP This will build My::App using only the prerequisites that are on the \&\f(CW\*(C`uri_upgrade\*(C'\fR stack. If the tests pass, then you can confidently upgrade \s-1URI\s0 on the \f(CW\*(C`dev\*(C'\fR stack as well. .PP As mentioned earlier, you can also use cpanm to install modules from your repository. But when installing from a stack other than the default, you must append \*(L"stacks/stack_name\*(R" to the \s-1URI.\s0 For example: .PP .Vb 1 \& cpanm \-\-mirror file:///home/jeff/repo/stacks/uri_upgrade \-\-mirror\-only My::App .Ve .SH "USING PINS" .IX Header "USING PINS" In the last section, we used a stack to experiment with upgrading a dependency. Fortunately, all the tests passed. But what if the tests didn't pass? If the problem lies within My-App and you can quickly correct it, you might just modify your code, release version 2.0 of My-App, and then proceed to upgrade \s-1URI\s0 on the \f(CW\*(C`master\*(C'\fR stack. .PP But if the issue is a bug in \s-1URI\s0 or it will take a long time to fix My-App, then you have a real problem. You don't want someone else to upgrade \s-1URI,\s0 nor do you want it to be upgraded inadvertently to satisfy some other prerequisite that My-App may have. Until the bug is fixed (in either \s-1URI\s0 or My-App) you need to prevent \s-1URI\s0 from being upgraded. This is what pins are for. .SS "Pinning a package" .IX Subsection "Pinning a package" When you pin a package, that version of the package is forced to stay in a stack. Any attempt to upgrade it (either directly or via another prerequisite) will fail. To pin a package, use the pin command like this: .PP .Vb 1 \& pinto \-\-root ~/repo pin URI .Ve .PP If you look at the listing for the \f(CW\*(C`master\*(C'\fR stack again, you'll see something like this: .PP .Vb 5 \& ... \& rl My::App 1.0 JEFF/My\-App\-1.0.tar.gz \& rf! URI 1.60 GAAS/URI\-1.60.tar.gz \& rf! URI::Escape 3.31 GAAS/URI\-1.60.tar.gz \& ... .Ve .PP The \*(L"!\*(R" near the beginning of the line indicates the package has been pinned. Notice every package in the \fI\s-1URI\-1.60\s0.tar.gz\fR distribution has been pinned, so it is impossible to partially upgrade a distribution (this situation could happen when a package moves into a different distribution). .SS "Unpinning a packages" .IX Subsection "Unpinning a packages" After a while, suppose you fix the problem in My-App or a new version of \s-1URI\s0 is released that fixes the bug. When that happens, you can unpin \s-1URI\s0 from the stack using the unpin command: .PP .Vb 1 \& pinto \-\-root ~/repo unpin URI .Ve .PP At this point you're free to upgrade \s-1URI\s0 to the latest version whenever you're ready. Just as with pinning, when you unpin a package, it unpins every other package it that distribution as well. .SH "USING PINS AND STACKS TOGETHER" .IX Header "USING PINS AND STACKS TOGETHER" Pins and stacks are used together to help manage change during the development cycle. For example, you could create a stack called \f(CW\*(C`prod\*(C'\fR that contains your known-good dependencies. Likewise, you could create a stack called \&\f(CW\*(C`dev\*(C'\fR that contains experimental dependencies for your next release. Initially, the \f(CW\*(C`dev\*(C'\fR stack is just a copy of the \f(CW\*(C`prod\*(C'\fR stack. .PP As development proceeds, you may upgrade or add several packages on the \f(CW\*(C`dev\*(C'\fR stack. If an upgraded package breaks your application, then you'll place a pin in that package on the \f(CW\*(C`prod\*(C'\fR stack to signal that it shouldn't be upgraded. .SS "Pins and Patches" .IX Subsection "Pins and Patches" Sometimes you may find that a new version of a \s-1CPAN\s0 distribution has a bug but the author is unable or unwilling to fix it (at least not before your next release is due). In that situation, you may elect to make a local patch of the \s-1CPAN\s0 distribution. .PP So suppose that you forked the code for \s-1URI\s0 and made a local version of the distribution called \fI\s-1URI\-1.60_PATCHED\s0.tar.gz\fR. You can add it to your repository using the add command: .PP .Vb 1 \& pinto \-\-root ~/repo add path/to/URI\-1.60_PATCHED.tar.gz .Ve .PP In this situation, it is wise to pin the package as well, since you do not want it to be updated until you are sure that the new release includes your patch or the author has fixed the bug by other means. .PP .Vb 1 \& pinto \-\-root ~/repo pin URI .Ve .PP When the author of \s-1URI\s0 releases version 1.62 with your patch, you'll want to try it before deciding to unpin from your locally patched version. Just as before, this can be done by cloning the stack with the copy command. Let's call it the \f(CW\*(C`trial\*(C'\fR stack this time: .PP .Vb 1 \& pinto \-\-root ~/repo copy master trial .Ve .PP But before you can update \s-1URI\s0 on the \f(CW\*(C`trial\*(C'\fR stack, you'll have to unpin it there: .PP .Vb 1 \& pinto \-\-root ~/repo unpin \-\-stack trial URI .Ve .PP Now you can proceed to update \s-1URI\s0 on the stack and try building \f(CW\*(C`My::App\*(C'\fR like this: .PP .Vb 2 \& pinto \-\-root ~/repo update \-\-stack trial URI \& pinto \-\-root ~/repo install \-\-stack trial My::App .Ve .PP If all the tests pass, then you can merge the changes back to the \f(CW\*(C`master\*(C'\fR stack: .PP .Vb 1 \& pinto \-\-root ~/repo merge trial master .Ve .SS "Reviewing Past Changes" .IX Subsection "Reviewing Past Changes" As you've noticed by now, each command that changes the state of a stack requires a log message to describe it. You can review those messages using the log command: .PP .Vb 1 \& pinto \-\-root ~/repo log .Ve .PP That should display something like this: .PP .Vb 3 \& revision 4a62d7ce\-245c\-45d4\-89f8\-987080a90112 \& Date: Mar 15, 2013 1:58:05 PM \& User: jeff \& \& Pin GAAS/URI\-1.59.tar.gz \& \& Pinning URI because it is not causes our foo.t script to fail \& \& revision 4a62d7ce\-245c\-45d4\-89f8\-987080a90112 \& Date: Mar 15, 2013 1:58:05 PM \& User: jeff \& \& Pull GAAS/URI\-1.59.tar.gz \& \& URI is required for HTTP support in our application \& \& ... .Ve .PP The header for each message shows who made the change and when it happened. It also has a unique identifier similar to Git's \s-1SHA\-1\s0 digests. You can use these identifiers to see the diffs between different revisions or to reset the stack back to a prior revision [\s-1NB:\s0 this feature is not actually implemented yet]. .SH "CONCLUSION" .IX Header "CONCLUSION" In this tutorial, you've seen the basic pinto commands for pulling dependencies into the repository, and adding your own distributions to the repository. You've also seen how to use stacks and pins to manage your dependencies in the face of some common development obstacles. .PP Each command has several options that were not discussed in this tutorial, and there are some commands that were not mentioned here at all. So you are encouraged to explore the manual pages for each command and learn more. .SH "SEE ALSO" .IX Header "SEE ALSO" Pinto::Manual::QuickStart .PP Pinto::Manual::Installing .PP Pinto (the library) .PP pinto (the application) .SH "AUTHOR" .IX Header "AUTHOR" Jeffrey Ryan Thalhammer .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" This software is copyright (c) 2015 by Jeffrey Ryan Thalhammer. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.