pax_global_header 0000666 0000000 0000000 00000000064 13557572301 0014522 g ustar 00root root 0000000 0000000 52 comment=f6179433410e8e8260b871ca127aae8942a988b0
zef-0.8.2/ 0000775 0000000 0000000 00000000000 13557572301 0012315 5 ustar 00root root 0000000 0000000 zef-0.8.2/.appveyor.yml 0000664 0000000 0000000 00000006014 13557572301 0014764 0 ustar 00root root 0000000 0000000 os: Visual Studio 2015
platform: x64
install:
- '"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64'
- appveyor-retry choco install strawberryperl --allow-empty-checksums
- SET PATH=C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH%
- appveyor-retry git clone https://github.com/rakudo/rakudo.git %APPVEYOR_BUILD_FOLDER%\..\rakudo
- cd %APPVEYOR_BUILD_FOLDER%\..\rakudo
- perl Configure.pl --gen-moar --gen-nqp --backends=moar
- nmake install
- SET PATH=%APPVEYOR_BUILD_FOLDER%\..\rakudo\install\bin;%PATH%
- SET PATH=%APPVEYOR_BUILD_FOLDER%\..\rakudo\install\share\perl6\site\bin;%PATH%
- cd %APPVEYOR_BUILD_FOLDER%
build: off
test_script:
- perl6 -I. bin/zef --version
# run xtests
- perl6 -I. xt/repository.t
- perl6 -I. xt/install.t
# test explicitly via `prove t/*` and `perl6 t/foo.t && perl6 t/bar.t`
# both should work, since all our CI envs have prove
- perl6 -I. bin/zef --debug --/tap-harness --/prove --perl6-test test .
- perl6 -I. bin/zef --debug --/tap-harness --prove --/perl6-test test .
# run relative local path test + install
- perl6 -I. bin/zef --debug install .
# test uninstall
- perl6 -I. bin/zef uninstall zef
# run absolute local path test + install
- perl6 -I. bin/zef install %APPVEYOR_BUILD_FOLDER%
# change path to make sure next `zef` commands aren't using any files in cwd or lib/
- cd %APPVEYOR_BUILD_FOLDER%\..
- zef update
# test informational commands
- zef --version
- zef --help
- zef locate Zef::CLI
- zef locate lib/Zef/CLI.pm6
- zef browse zef bugtracker --/open
- zef info zef
# test bells and whistles
- zef --debug test ./zef
- zef --debug search Base64
- zef --debug rdepends Base64
- zef --debug depends Cro::SSL
- zef --debug fetch Base64
# test installing from what `fetch` put in ::LocalCache
- zef --debug --/cpan --/p6c install Base64
- zef --debug --max=10 list
- zef --debug --installed list
- zef --debug --force-install install Base64
# test tar + upgrade
- zef --debug install https://github.com/ugexe/Perl6-PathTools/archive/0434191c56e0f3254ab1d756d90f9191577de5a0.tar.gz
- zef --debug upgrade PathTools
# test zip
- zef --debug install https://github.com/ugexe/Perl6-Text--Table--Simple/archive/v0.0.3.zip
# test remote git repo + tag
- zef --debug install https://github.com/ugexe/Perl6-Text--Table--Simple.git@v0.0.4
# Test self contained installation
- zef install Distribution::Common --/test
- zef install Distribution::Common::Remote -to=inst#foo --contained --/test
- zef uninstall Distribution::Common
- perl6 -I inst#foo -M Distribution::Common::Remote::Github -e ""
- zef --/confirm nuke TempDir StoreDir RootDir
- zef update cached # test single repository update; should be 0 after previous nuke
- perl6 -I %APPVEYOR_BUILD_FOLDER% %APPVEYOR_BUILD_FOLDER%/bin/zef --/confirm nuke site home
shallow_clone: true
zef-0.8.2/.circleci/ 0000775 0000000 0000000 00000000000 13557572301 0014150 5 ustar 00root root 0000000 0000000 zef-0.8.2/.circleci/config.yml 0000664 0000000 0000000 00000007023 13557572301 0016142 0 ustar 00root root 0000000 0000000 version: 2
variables:
macos: &macos
macos:
xcode: "10.2.0"
linux: &linux
machine: true
install-rakudo: &install-rakudo
run:
name: Build and install rakudo
command: |
git clone https://github.com/rakudo/rakudo.git $HOME/rakudo
cd $HOME/rakudo
perl Configure.pl --gen-moar --gen-nqp --make-install
test-zef: &test-zef
run:
name: Run tests
command: |
perl6 -I. bin/zef --version
# run xtests
perl6 -I. xt/repository.t
perl6 -I. xt/install.t
# test explicitly via `prove t/*` and `perl6 t/foo.t && perl6 t/bar.t`
# both should work, since all our CI envs have prove
perl6 -I. bin/zef --debug --/tap-harness --/prove --perl6-test test .
perl6 -I. bin/zef --debug --/tap-harness --prove --/perl6-test test .
# run relative local path test + install
perl6 -I. bin/zef --debug install .
# test uninstall
perl6 -I. bin/zef uninstall zef
# run absolute local path test + install
perl6 -I. bin/zef install $PWD
# change path to make sure next `zef` commands aren't using any files in cwd or lib/
(cd .. && zef update)
# test informational commands
zef --version
zef --help
zef locate Zef::CLI
zef locate lib/Zef/CLI.pm6
zef browse zef bugtracker --/open
zef info zef
# test bells and whistles
zef --debug test .
zef --debug search Base64
zef --debug rdepends Base64
zef --debug depends Cro::SSL
zef --debug fetch Base64
# test installing from what `fetch` put in ::LocalCache
zef --debug --/cpan --/p6c install Base64
zef --debug --max=10 list
zef --debug --installed list
zef --debug --force-install install Base64
# test tar + upgrade
zef --debug install https://github.com/ugexe/Perl6-PathTools/archive/0434191c56e0f3254ab1d756d90f9191577de5a0.tar.gz
zef --debug upgrade PathTools
# test zip
zef --debug install https://github.com/ugexe/Perl6-Text--Table--Simple/archive/v0.0.3.zip
# test remote git repo + tag
zef --debug install https://github.com/ugexe/Perl6-Text--Table--Simple.git@v0.0.4
# Test self contained installation
zef install Distribution::Common --/test
zef install Distribution::Common::Remote -to=inst#foo --contained --/test
zef uninstall Distribution::Common
perl6 -I inst#foo -M Distribution::Common::Remote::Github -e ''
zef --/confirm nuke TempDir StoreDir RootDir
zef update cached # test single repository update; should be 0 after previous nuke
perl6 -I /home/circleci/project /home/circleci/project/bin/zef --/confirm nuke site home
jobs:
test-linux:
<<: *linux
environment:
ZEF_PLUGIN_DEBUG: 1
ZEF_BUILDPM_DEBUG: 1
PATH: /home/circleci/rakudo/install/share/perl6/site/bin:/home/circleci/rakudo/install/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
- checkout
- *install-rakudo
- *test-zef
#test-macos:
# <<: *macos
# environment:
# ZEF_PLUGIN_DEBUG: 1
# ZEF_BUILDPM_DEBUG: 1
# PATH: /Users/circleci/rakudo/install/share/perl6/site/bin:/Users/circleci/rakudo/install/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# steps:
# - checkout
# - *install-rakudo
# - *test-zef
workflows:
version: 2
test:
jobs:
- test-linux
#- test-macos
zef-0.8.2/.github/ 0000775 0000000 0000000 00000000000 13557572301 0013655 5 ustar 00root root 0000000 0000000 zef-0.8.2/.github/FUNDING.yml 0000664 0000000 0000000 00000000020 13557572301 0015462 0 ustar 00root root 0000000 0000000 github: [ugexe]
zef-0.8.2/.github/ISSUE_TEMPLATE.md 0000664 0000000 0000000 00000001133 13557572301 0016360 0 ustar 00root root 0000000 0000000
## Context
## Expected Behavior
## Actual Behavior
## Steps to Reproduce
## Your Environment
* perl6 -v
* zef list --installed
zef-0.8.2/.gitignore 0000664 0000000 0000000 00000000054 13557572301 0014304 0 ustar 00root root 0000000 0000000 *.moarvm
*.jar
tmp/
lib/.precomp/
.precomp/
zef-0.8.2/.travis.yml 0000664 0000000 0000000 00000006026 13557572301 0014432 0 ustar 00root root 0000000 0000000 language: perl
os:
- linux
- osx
env:
- BACKEND=moar
# - BACKEND=jvm
matrix:
allow_failures:
- env: BACKEND=jvm
fast_finish: true
sudo: false
before_install:
- git clone https://github.com/rakudo/rakudo.git $HOME/rakudo
- cd $HOME/rakudo
- 'if [[ $BACKEND == "moar" ]]; then export OPTS="--gen-moar --gen-nqp --backends=moar"; fi'
- 'if [[ $BACKEND == "jvm" ]]; then export OPTS="--gen-nqp --backends=jvm"; fi'
- perl Configure.pl $OPTS
- make install
- export PATH=$HOME/rakudo/install/bin:$PATH
- export ZEF_BUILDPM_DEBUG=1
- export PATH=$HOME/rakudo/install/share/perl6/site/bin:$PATH
- cd $TRAVIS_BUILD_DIR
install:
# need at least 1 statement in 'install'
- perl6 -v
script:
- perl6 -I. bin/zef --version
# run xtests
- perl6 -I. xt/repository.t
- perl6 -I. xt/install.t
# test explicitly via `prove t/*` and `perl6 t/foo.t && perl6 t/bar.t`
# both should work, since all our CI envs have prove
- perl6 -I. bin/zef --debug --/tap-harness --/prove --perl6-test test .
- perl6 -I. bin/zef --debug --/tap-harness --prove --/perl6-test test .
# run relative local path test + install
- perl6 -I. bin/zef --debug install .
# test uninstall
- perl6 -I. bin/zef uninstall zef
# run absolute local path test + install
- perl6 -I. bin/zef install $TRAVIS_BUILD_DIR
# change path to make sure next `zef` commands aren't using any files in cwd or lib/
- cd $TRAVIS_BUILD_DIR/..
- zef update
# test informational commands
- zef --version
- zef --help
- zef locate Zef::CLI
- zef locate lib/Zef/CLI.pm6
- zef browse zef bugtracker --/open
- zef info zef
# test bells and whistles
- zef --debug test ./zef
- zef --debug search Base64
- zef --debug rdepends Base64
- zef --debug depends Cro::SSL
- zef --debug fetch Base64
# test installing from what `fetch` put in ::LocalCache
- zef --debug --/cpan --/p6c install Base64
- zef --debug --max=10 list
- zef --debug --installed list
- zef --debug --force-install install Base64
# test tar + upgrade
- zef --debug install https://github.com/ugexe/Perl6-PathTools/archive/0434191c56e0f3254ab1d756d90f9191577de5a0.tar.gz
- zef --debug upgrade PathTools
# test zip
- zef --debug install https://github.com/ugexe/Perl6-Text--Table--Simple/archive/v0.0.3.zip
# test remote git repo + tag
- zef --debug install https://github.com/ugexe/Perl6-Text--Table--Simple.git@v0.0.4
# Test self contained installation
- zef install Distribution::Common --/test
- zef install Distribution::Common::Remote -to=inst#foo --contained --/test
- zef uninstall Distribution::Common
- perl6 -I inst#foo -M Distribution::Common::Remote::Github -e ''
- zef --/confirm nuke TempDir StoreDir RootDir
- zef update cached # test single repository update; should be 0 after previous nuke
- perl6 -I $TRAVIS_BUILD_DIR $TRAVIS_BUILD_DIR/bin/zef --/confirm nuke site home
zef-0.8.2/LICENSE 0000664 0000000 0000000 00000021306 13557572301 0013324 0 ustar 00root root 0000000 0000000 The Artistic License 2.0
Copyright (c) 2000-2006, The Perl Foundation.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
This license establishes the terms under which a given free software
Package may be copied, modified, distributed, and/or redistributed.
The intent is that the Copyright Holder maintains some artistic
control over the development of that Package while still keeping the
Package available as open source and free software.
You are always permitted to make arrangements wholly outside of this
license directly with the Copyright Holder of a given Package. If the
terms of this license do not permit the full use that you propose to
make of the Package, you should contact the Copyright Holder and seek
a different licensing arrangement.
Definitions
"Copyright Holder" means the individual(s) or organization(s)
named in the copyright notice for the entire Package.
"Contributor" means any party that has contributed code or other
material to the Package, in accordance with the Copyright Holder's
procedures.
"You" and "your" means any person who would like to copy,
distribute, or modify the Package.
"Package" means the collection of files distributed by the
Copyright Holder, and derivatives of that collection and/or of
those files. A given Package may consist of either the Standard
Version, or a Modified Version.
"Distribute" means providing a copy of the Package or making it
accessible to anyone else, or in the case of a company or
organization, to others outside of your company or organization.
"Distributor Fee" means any fee that you charge for Distributing
this Package or providing support for this Package to another
party. It does not mean licensing fees.
"Standard Version" refers to the Package if it has not been
modified, or has been modified only in ways explicitly requested
by the Copyright Holder.
"Modified Version" means the Package, if it has been changed, and
such changes were not explicitly requested by the Copyright
Holder.
"Original License" means this Artistic License as Distributed with
the Standard Version of the Package, in its current version or as
it may be modified by The Perl Foundation in the future.
"Source" form means the source code, documentation source, and
configuration files for the Package.
"Compiled" form means the compiled bytecode, object code, binary,
or any other form resulting from mechanical transformation or
translation of the Source form.
Permission for Use and Modification Without Distribution
(1) You are permitted to use the Standard Version and create and use
Modified Versions for any purpose without restriction, provided that
you do not Distribute the Modified Version.
Permissions for Redistribution of the Standard Version
(2) You may Distribute verbatim copies of the Source form of the
Standard Version of this Package in any medium without restriction,
either gratis or for a Distributor Fee, provided that you duplicate
all of the original copyright notices and associated disclaimers. At
your discretion, such verbatim copies may or may not include a
Compiled form of the Package.
(3) You may apply any bug fixes, portability changes, and other
modifications made available from the Copyright Holder. The resulting
Package will still be considered the Standard Version, and as such
will be subject to the Original License.
Distribution of Modified Versions of the Package as Source
(4) You may Distribute your Modified Version as Source (either gratis
or for a Distributor Fee, and with or without a Compiled form of the
Modified Version) provided that you clearly document how it differs
from the Standard Version, including, but not limited to, documenting
any non-standard features, executables, or modules, and provided that
you do at least ONE of the following:
(a) make the Modified Version available to the Copyright Holder
of the Standard Version, under the Original License, so that the
Copyright Holder may include your modifications in the Standard
Version.
(b) ensure that installation of your Modified Version does not
prevent the user installing or running the Standard Version. In
addition, the Modified Version must bear a name that is different
from the name of the Standard Version.
(c) allow anyone who receives a copy of the Modified Version to
make the Source form of the Modified Version available to others
under
(i) the Original License or
(ii) a license that permits the licensee to freely copy,
modify and redistribute the Modified Version using the same
licensing terms that apply to the copy that the licensee
received, and requires that the Source form of the Modified
Version, and of any works derived from it, be made freely
available in that license fees are prohibited but Distributor
Fees are allowed.
Distribution of Compiled Forms of the Standard Version
or Modified Versions without the Source
(5) You may Distribute Compiled forms of the Standard Version without
the Source, provided that you include complete instructions on how to
get the Source of the Standard Version. Such instructions must be
valid at the time of your distribution. If these instructions, at any
time while you are carrying out such distribution, become invalid, you
must provide new instructions on demand or cease further distribution.
If you provide valid instructions or cease distribution within thirty
days after you become aware that the instructions are invalid, then
you do not forfeit any of your rights under this license.
(6) You may Distribute a Modified Version in Compiled form without
the Source, provided that you comply with Section 4 with respect to
the Source of the Modified Version.
Aggregating or Linking the Package
(7) You may aggregate the Package (either the Standard Version or
Modified Version) with other packages and Distribute the resulting
aggregation provided that you do not charge a licensing fee for the
Package. Distributor Fees are permitted, and licensing fees for other
components in the aggregation are permitted. The terms of this license
apply to the use and Distribution of the Standard or Modified Versions
as included in the aggregation.
(8) You are permitted to link Modified and Standard Versions with
other works, to embed the Package in a larger work of your own, or to
build stand-alone binary or bytecode versions of applications that
include the Package, and Distribute the result without restriction,
provided the result does not expose a direct interface to the Package.
Items That are Not Considered Part of a Modified Version
(9) Works (including, but not limited to, modules and scripts) that
merely extend or make use of the Package, do not, by themselves, cause
the Package to be a Modified Version. In addition, such works are not
considered parts of the Package itself, and are not subject to the
terms of this license.
General Provisions
(10) Any use, modification, and distribution of the Standard or
Modified Versions is governed by this Artistic License. By using,
modifying or distributing the Package, you accept this license. Do not
use, modify, or distribute the Package, if you do not accept this
license.
(11) If your Modified Version has been derived from a Modified
Version made by someone other than you, you are nevertheless required
to ensure that your Modified Version complies with the requirements of
this license.
(12) This license does not grant you the right to use any trademark,
service mark, tradename, or logo of the Copyright Holder.
(13) This license includes the non-exclusive, worldwide,
free-of-charge patent license to make, have made, use, offer to sell,
sell, import and otherwise transfer the Package with respect to any
patent claims licensable by the Copyright Holder that are necessarily
infringed by the Package. If you institute patent litigation
(including a cross-claim or counterclaim) against any party alleging
that the Package constitutes direct or contributory patent
infringement, then this Artistic License to you shall terminate on the
date that such litigation is filed.
(14) Disclaimer of Warranty:
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
zef-0.8.2/META6.json 0000664 0000000 0000000 00000007107 13557572301 0014031 0 ustar 00root root 0000000 0000000 {
"meta-version" : "0",
"perl" : "6.c",
"name" : "zef",
"api" : "0",
"version" : "0.8.2",
"auth" : "github:ugexe",
"description" : "A Raku / Perl 6 module manager",
"license" : "Artistic-2.0",
"build-depends" : [ ],
"test-depends" : [ "Test" ],
"depends" : [ "NativeCall" ],
"provides" : {
"Zef" : "lib/Zef.pm6",
"Zef::Build" : "lib/Zef/Build.pm6",
"Zef::CLI" : "lib/Zef/CLI.pm6",
"Zef::Client" : "lib/Zef/Client.pm6",
"Zef::Config" : "lib/Zef/Config.pm6",
"Zef::Extract" : "lib/Zef/Extract.pm6",
"Zef::Identity" : "lib/Zef/Identity.pm6",
"Zef::Install" : "lib/Zef/Install.pm6",
"Zef::Test" : "lib/Zef/Test.pm6",
"Zef::Fetch" : "lib/Zef/Fetch.pm6",
"Zef::Report" : "lib/Zef/Report.pm6",
"Zef::Repository" : "lib/Zef/Repository.pm6",
"Zef::Repository::LocalCache" : "lib/Zef/Repository/LocalCache.pm6",
"Zef::Repository::Ecosystems" : "lib/Zef/Repository/Ecosystems.pm6",
"Zef::Distribution" : "lib/Zef/Distribution.pm6",
"Zef::Distribution::DependencySpecification" : "lib/Zef/Distribution/DependencySpecification.pm6",
"Zef::Distribution::Local" : "lib/Zef/Distribution/Local.pm6",
"Zef::Service::InstallPM6" : "lib/Zef/Service/InstallPM6.pm6",
"Zef::Service::FetchPath" : "lib/Zef/Service/FetchPath.pm6",
"Zef::Service::TAP" : "lib/Zef/Service/TAP.pm6",
"Zef::Service::P6CReporter" : "lib/Zef/Service/P6CReporter.pm6",
"Zef::Service::Shell::DistributionBuilder" : "lib/Zef/Service/Shell/DistributionBuilder.pm6",
"Zef::Service::Shell::LegacyBuild" : "lib/Zef/Service/Shell/LegacyBuild.pm6",
"Zef::Service::Shell::Test" : "lib/Zef/Service/Shell/Test.pm6",
"Zef::Service::Shell::prove" : "lib/Zef/Service/Shell/prove.pm6",
"Zef::Service::Shell::unzip" : "lib/Zef/Service/Shell/unzip.pm6",
"Zef::Service::Shell::tar" : "lib/Zef/Service/Shell/tar.pm6",
"Zef::Service::Shell::p5tar" : "lib/Zef/Service/Shell/p5tar.pm6",
"Zef::Service::Shell::curl" : "lib/Zef/Service/Shell/curl.pm6",
"Zef::Service::Shell::git" : "lib/Zef/Service/Shell/git.pm6",
"Zef::Service::Shell::wget" : "lib/Zef/Service/Shell/wget.pm6",
"Zef::Service::Shell::PowerShell" : "lib/Zef/Service/Shell/PowerShell.pm6",
"Zef::Service::Shell::PowerShell::download" : "lib/Zef/Service/Shell/PowerShell/download.pm6",
"Zef::Service::Shell::PowerShell::unzip" : "lib/Zef/Service/Shell/PowerShell/unzip.pm6",
"Zef::Utils::FileSystem" : "lib/Zef/Utils/FileSystem.pm6",
"Zef::Utils::SystemInfo" : "lib/Zef/Utils/SystemInfo.pm6",
"Zef::Utils::SystemQuery" : "lib/Zef/Utils/SystemQuery.pm6",
"Zef::Utils::URI" : "lib/Zef/Utils/URI.pm6"
},
"resources" : [
"config.json",
"scripts/perl5tar.pl",
"scripts/win32http.ps1",
"scripts/win32unzip.ps1"
],
"authors" : [
"Nick Logan",
"Tony O'Dell"
],
"support" : {
"bugtracker" : "https://github.com/ugexe/zef/issues",
"source" : "https://github.com/ugexe/zef.git"
},
"tags" : [
"package-manager",
"module-installer",
"meta-search",
"distribution",
"ecosystem",
"cpan",
"toolchain"
]
}
zef-0.8.2/README.pod 0000664 0000000 0000000 00000046157 13557572301 0013773 0 ustar 00root root 0000000 0000000 =pod
=encoding utf8
=head2 Zef
Raku / Perl6 Module Management
=for HTML
=head1 Installation
=head4 Manual
$ git clone https://github.com/ugexe/zef.git
$ cd zef
$ perl6 -I. bin/zef install .
=head4 Rakudobrew
To install via rakudobrew, please use the following command:
$ rakudobrew build zef
=head1 USAGE
zef --help
zef --version
# install the CSV::Parser distribution
zef install CSV::Parser
# search for distribution names matching `CSV`
zef search CSV
# detailed information for a matching distribution
zef info CSV::Parser
# list all available distributions
zef list
# list reverse dependencies of an identity
zef rdepends HTTP::UserAgent
# test project in current directory
zef test .
# fetch a specific module only
zef fetch CSV::Parser
# fetch a module, then shell into its local path
zef look CSV::Parser
# smoke test modules from all repositories
zef smoke
# run Build.pm if one exists in given path
zef build .
# update Repository package lists
zef update
# upgrade all distributions (BETA)
zef upgrade
# upgrade specific distribution (BETA)
zef upgrade CSV::Parser
# lookup module info by name/path/sha1
zef --sha1 locate 9FA0AC28824EE9E5A9C0F99951CA870148AE378E
# launch browser to named support urls from meta data
zef browse zef bugtracker
=head2 More CLI
=head4 B [*@identities]
Note: The install process does not install anything until all phases have completed. So, if the user requested to
C, and A required module B: both would be downloaded, potentially built, tested, and installed -- but only
if both passed all their tests. For example: if module A failed its tests, then module B would not be installed
(even if it passed its own tests) unless forced.
[C<@identities>] can take the form of a file path (starting with B<.> or B>), URLs, paths, or identities:
# IDENTITY
zef install CSV::Parser
zef install "CSV::Parser:auth:ver<0.1.2>"
zef install "CSV::Parser:ver<0.1.2>"
# PATH
zef install ./Perl6-Net--HTTP
# URL
zef -v install git://github.com/ugexe/zef.git
zef -v install https://github.com/ugexe/zef/archive/master.tar.gz
zef -v install https://github.com/ugexe/zef.git@v0.1.22
A request may contain any number and combination of these. Paths and URLs will be resolved first so they are available
to fulfill any dependencies of other requested identities.
B
# Install to a custom locations
--install-to= # site/home/vendor/perl, or
-to= # inst#/home/some/path/custom
# Install all transitive and direct dependencies
# even if they are already installed globally (BETA)
--contained
# Load a specific Zef config file
--config-path=/some/path/config.json
# Install only the dependency chains of the requested distributions
--deps-only
# Ignore errors occuring during the corresponding phase
--force-resolve
--force-fetch
--force-extract
--force-build
--force-test
--force-install
# or set the default to all unset --force-* flags to True
--force
# Set the timeout for corresponding phases
--fetch-timeout=600
--extract-timeout=3600
--build-timeout=3600
--test-timeout=3600
--install-timeout=3600
# Number of simultaneous distributions/jobs to process for the corresponding phases
--fetch-degree=5
--test-degree=1
# or set the default to all unset --*-timeout flags to 0
--timeout=0
# Do everything except the actual installations
--dry
# Build/Test/Install each dependency serially before proceeding to Build/Test/Install the next
--serial
# Disable testing
--/test
# Disable build phase
--/build
# Disable fetching dependencies
--/depends
--/build-depends
--/test-depends
# Force a refresh for all module index indexes
--update
# Force a refresh for a specific ecosystem module index
--update=[ecosystem]
# Skip refreshing all module index indexes
--/update
# Skip refreshing for a specific ecosystem module index
--/update=[ecosystem]
B
# Number of simultaneous distributions/jobs to process for the corresponding phases (see: --[phase]-degree options)
ZEF_FETCH_DEGREE=5
ZEF_TEST_DEGREE=1
# Set the timeout for corresponding phases (see: --[phase]-timeout options)
ZEF_FETCH_TIMEOUT=600
ZEF_EXTRACT_TIMEOUT=3600
ZEF_BUILD_TIMEOUT=3600
ZEF_TEST_TIMEOUT=3600
ZEF_INSTALL_TIMEOUT=3600
=head4 B [*@identities]
Uninstall the specified distributions
Note: Requires a bleeding edge rakudo (not available in 6.c)
=head4 B
Update the package indexes for all C backends
Note: Some C backends, like the default Ecosystems, have an C option
in C that can be enabled. This should be the number of hours until it should
auto update based on the file system last modified time of the ecosystem json file location.
=head4 B [*@identities] I
Upgrade specified identities. If no identities are provided, zef attempts to upgrade all installed distributions.
=head4 B [$identity]
How these are handled depends on the C engine used, which by default is Cp6cE>
$ zef -v --cpan --metacpan search URI
===> Found 4 results
-------------------------------------------------------------------------
ID|From |Package |Description
-------------------------------------------------------------------------
1 |Zef::Repository::LocalCache |URI:ver<0.1.1> |A URI impleme...
2 |Zef::Repository::Ecosystems |URI:ver<0.1.1> |A URI impleme...
3 |Zef::Repository::Ecosystems |URI:ver<0.1.1> |A URI impleme...
4 |Zef::Repository::Ecosystems |URI:ver<0.000.001>|A URI impleme...
-------------------------------------------------------------------------
=head4 B [$identity]
View meta information of a distribution
$ zef -v info HTTP::UserAgent
- Info for: HTTP::UserAgent
- Identity: HTTP::UserAgent:ver<1.1.16>:auth
- Recommended By: Zef::Repository::LocalCache
Author: github:sergot
Description: Web user agent
Source-url: git://github.com/sergot/http-useragent.git
Provides: 11 modules
# HTTP::Cookie
# HTTP::Header
# HTTP::Cookies
# HTTP::Message
# HTTP::Request
# HTTP::Response
# HTTP::MediaType
# HTTP::UserAgent
# HTTP::Header::Field
# HTTP::Request::Common
# HTTP::UserAgent::Common
Depends: 7 items
---------------------------------
ID|Identity |Installed?
---------------------------------
1 |HTTP::Status |✓
2 |File::Temp |✓
3 |DateTime::Parse |✓
4 |Encode |✓
5 |MIME::Base64 |✓
6 |URI |✓
7 |IO::Capture::Simple|✓
---------------------------------
B
# Extra details (eg, list dependencies and which ones are installed)
-v
=head4 B [*@from]
List known available distributions
$ zef --installed list
===> Found via /home/nickl/.rakudobrew/moar-master/install/share/perl6/site
CSV::Parser:ver<0.1.2>:auth
Zef:auth
===> Found via /home/nickl/.rakudobrew/moar-master/install/share/perl6
CORE:ver<6.c>:auth
Note that not every Repository may provide such a list, and such lists may only
be a subset. For example: We may not be able to get a list of every distribution
on metacpan, but we *can* get the $x most recent additions (we use 100 for now).
[C<@from>] allows you to show results from specific repositories only:
zef --installed list perl # Only list modules installed by rakudo itself
zef list cpan # Only show available modules from the repository
zef list p6c # with a name field matching the arguments to `list`
zef list cached # (be sure the repository is enabled in config)
Otherwise results from all enabled repositories will be returned.
B
# Only list installed distributions
--installed
# Additionally list the modules of discovered distributions
-v
=head4 B [$identity]
List direct and transitive dependencies to the first successful build graph for C<$identity>
$ zef depends Cro::SSL
Cro::Core:ver<0.7>
IO::Socket::Async::SSL:ver<0.3>
OpenSSL:ver<0.1.14>:auth
=head4 B [$identity]
List available distributions that directly depend on C<$identity>
$ zef rdepends Net::HTTP
Minecraft-Tools:ver<0.1.0>
LendingClub:ver<0.1.0>
=head4 B [*@identities]
Fetches candidates for given identities
=head4 B [*@paths]
Run tests on each distribution located at [C<@paths>]
=head4 B [*@paths]
Run the Build.pm file located in the given [C<@paths>]
If you want to create a build hook, put the following dependency-free boilerplate
in a file named C at the root of your distribution:
class Build {
method build($dist-path) {
# do build stuff to your module
# which is located at $dist-path
}
}
Set the env variable B or use the I<--debug> flag for additional debugging information.
I
=head4 B [$identity]
Fetches the requested distribution and any dependencies (if requested), changes the directory to that of the fetched
distribution, and then stops program execution. This allows you modify or look at the source code before manually
continuing the install via C
Note that the path to any dependencies that needed to be fetched will be set in env at B, so you should
be able to run any build scripts, tests, or complete a manual install without having to specify their locations.
=head4 B $identity [bugtracker | homepage | source]
B
# disables launching a browser window (just shows url)
--/open
Output the url and launch a browser to open it.
# also opens browser
$ zef browse Net::HTTP bugtracker
https://github.com/ugexe/Perl6-Net--HTTP/issues
# only outputs the url
$ zef browse Net::HTTP bugtracker --/open
https://github.com/ugexe/Perl6-Net--HTTP/issues
=head4 B [$identity, $name-path, $sha1-id]
B
# The argument is a sha1-id (otherwise assumed to be an identity or name-path)
--sha1
Lookup a locally installed module by $identity, $name-path, or $sha1-id
$ zef --sha1 locate A9948E7371E0EB9AFDF1EEEB07B52A1B75537C31
===> From Distribution: zef:ver<*>:auth:api<>
lib/Zef/CLI.pm6 => ~/rakudo/install/share/perl6/site/sources/A9948E7371E0EB9AFDF1EEEB07B52A1B75537C31
$ zef locate Zef::CLI
===> From Distribution: zef:ver<*>:auth:api<>
lib/Zef/CLI.pm6 => ~/rakudo/install/share/perl6/site/sources/A9948E7371E0EB9AFDF1EEEB07B52A1B75537C31
$ zef locate lib/Zef/CLI.pm6
===> From Distribution: zef:ver<*>:auth:api<>
Zef::CLI => ~/rakudo/install/share/perl6/site/sources/A9948E7371E0EB9AFDF1EEEB07B52A1B75537C31
=head4 B [RootDir | TempDir | StoreDir]
Deletes all paths in the specific configuration directory
=head4 B [site | home]
Deletes all paths that are rooted in the prefix of the matching CompUnit::Repository name
# uninstall all modules
$ zef nuke site home
=head2 Output Verbosity
You can control the logging level using the following flags:
# More/less detailed output
--error, --warn, --info (default), --verbose (-v), --debug
=head1 Global Configuration
=head3 Finding the configuration file
You can always see the configuration file that will be used by running:
$ zef --help
In most cases the default configuration combined with command line options should be enough for most users.
If you are most users (e.g. not: power users, packagers, zef plugin developers) you hopefully don't care about this section!
=head3 How the configuration file is chosen
The configuration file will be chosen at runtime from one of two (technically four) locations, and one can affect the others (this is not really a design decision and suggestions and PRs are welcome).
First, and the most precise way, is to specify the config file by passing C<--config-path="..."> to any zef command.
Second, third, and fourth we look at the path pointed to by C<%?RESOURCESEconfig.jsonE>. This will point to C<$zef-dir/resources/config.json>, where C<$zef-dir> will be either:
=over 4
=item * The prefix of a common configuration directory, such as C<$XDG_CONFIG_HOME> or C<$HOME/.config>.
=item * The prefix of a rakudo installation location - This is the case if the modules loaded for bin/zef come from an installation CompUnit::Repository.
=item * The current working directory C<$*CWD> - This is the case when modules loaded for bin/zef come from a non-installation CompUnit::Repository (such as C<-I $dist-path>).
To understand how this is chosen, consider:
# Modules not loaded from an ::Installation,
# so %?RESOURCES is $*CWD/resources
$ perl6 -I. bin/zef --help
...
CONFIGURATION /home/user/perl6/zef/resources/config.json
...
# Installed zef script loads modules from an ::Installation,
# so %?RESOURCES is $perl6-share-dir/site/resources
$ zef --help
...
CONFIGURATION /home/user/perl6/install/share/perl6/site/resources/EE5DBAABF07682ECBE72BEE98E6B95E5D08675DE.json
...
=back
This config is loaded, but it is not yet the chosen config! We check that temporary config's C<%configERootDirE> for valid json in a file named C (i.e. C<%configERootDirE/config.json>). This can be confusing (so it may go away or be refined - PRs welcome) but for most cases it just means C<$*HOME/.zef/config.json> will override an installed zef configuration file.
To summarize:
=over 4
=item * You can edit the C file before you install zef.
When you C that configuration file be be used to install zef and will also be installed with zef such that it will be the default.
=item * You can create a C<%configERootDirE/config.json> file.
Where C<%configERootDirE>
comes from the previously mentioned C<%?RESOURCESEconfig.jsonE>'s C field (C<$*HOME/.zef> in most cases), to allow overriding zef config behavior on a per user basis (allows setting different C<--install-to> targets for, say, a root user and a regular user). Since this new config file could have a different C than the default config (used to find the new one in the first place) this behavior may be changed in the future to be less confusing.
=item * You can override both of the previous entries by passing Cany commandE>
=back
=head3 Configuration fields
=head4 Basic Settings
=over 4
=item * B - Where zef will look for a custom config.json file
=item * B - A staging area for items that have been fetched and need to be extracted/moved
=item * B - Where zef caches distributions, package lists, etc after they've been fetched and extracted
=item * B - This sets the default value for C<--install-to="...">. The default value of C means it will first try installing to rakudo's installation prefix, and if its not writable by the current user it will install to C<$*HOME/.perl6>. These directories are not chosen by zef - they are actually represented by the magic strings C and C (which, like C, are valid values despite not being paths along with C and C)
=back
=head4 Phases / Plugins Settings
These consist of an array of hashes that describe how to instantiate some class that fulfills the appropriate interface from I (C C C C C)
The descriptions follow this format:
{
"short-name" : "p6c",
"enabled" : 1,
"module" : "Zef::Repository::Ecosystems",
"options" : { }
}
and are instantiated via
::($hash).new(|($hash)
=over 4
=item * B - This adds an enable and disable flag by the same name to the CLI (e.g. C<--p6c> and C<--/p6c>) and is used when referencing which object took some action.
=item * B - Set to 0 to skip over the object during consideration (it will never be loaded). If omitted or if the value is non 0 then it will be enabled for use.
=item * B - The name of the class to instantiate. While it doesn't technically have to be a module it I need to be a known namespace to C.
=item * B - These are passed to the objects C method and may not be consistent between modules as they are free to implement their own requirements.
=back
See the configuration file in L for a
little more information on how plugins are invoked.
You can see debug output related to chosing and loading plugins by setting the env variable B
=head1 FAQ
=head3 CPAN?
CPAN is now used as a default (alongside the familiar p6c "ecosystem").
# Explicitly enable cpan (now defaults to the same as `zef search zef`)
$ zef --cpan search Inline::Perl5
=head3 Proxy support?
All the default fetching plugins have proxy support, but you'll need to refer to the backend program's
(wget, curl, git, etc) docs. You may need to set an I variable, or you may need to add a command line
option for that specific plugin in I
=head3 Custom installation locations?
Pass a path to the I<-to> / I<--install-to> option and prefix the path with C (unless you know what you're doing)
$ zef -to="inst#/home/perl6/custom" install Text::Table::Simple
===> Searching for: Text::Table::Simple
===> Testing: Text::Table::Simple:ver<0.0.3>:auth
===> Testing [OK] for Text::Table::Simple:ver<0.0.3>:auth
===> Installing: Text::Table::Simple:ver<0.0.3>:auth
To make the custom location discoverable:
# Set the PERL6LIB env:
$ PERL6LIB="inst#/home/perl6/custom" perl6 -e "use Text::Table::Simple; say 'ok'"
ok
# or simply include it as needed
$ perl6 -Iinst#/home/perl6/custom -e "use Text::Table::Simple; say 'ok'"
ok
=head3 Test reporting?
This feature can be enabled by passing `--p6ctesters` (and having C installed) or `--cpantesters` (and having C installed)
=cut
zef-0.8.2/bin/ 0000775 0000000 0000000 00000000000 13557572301 0013065 5 ustar 00root root 0000000 0000000 zef-0.8.2/bin/zef 0000775 0000000 0000000 00000000044 13557572301 0013575 0 ustar 00root root 0000000 0000000 #!/usr/bin/env perl6
use Zef::CLI;
zef-0.8.2/lib/ 0000775 0000000 0000000 00000000000 13557572301 0013063 5 ustar 00root root 0000000 0000000 zef-0.8.2/lib/Zef.pm6 0000664 0000000 0000000 00000013730 13557572301 0014237 0 ustar 00root root 0000000 0000000 class Zef { }
my @zrun-invoke = BEGIN $*DISTRO.is-win
?? ((%*ENV.first({.key.lc eq 'comspec'}).?value // 'cmd.exe').Str, '/x/d/c')
!! '';
sub zrun(*@_, *%_) is export { run (|@zrun-invoke, |@_).grep(*.?chars), |%_ }
sub zrun-async(*@_, *%_) is export { Proc::Async.new( (|@zrun-invoke, |@_).grep(*.?chars), |%_ ) }
# rakudo must be able to parse json, so it doesn't
# make sense to require a dependency to parse it
sub from-json($text) is export {
::("Rakudo::Internals::JSON").from-json($text)
}
sub to-json(|c) is export {
::("Rakudo::Internals::JSON").to-json(|c)
}
# todo: define all the additional options in these signatures, such as passing :$jobs
# to `test` (for the prove command), how to handle existing files, etc
# A way to avoid printing everything to make --quiet option more univesal between plugins
# Need to create a messaging format to include the phase, file, verbosity level, progress,
# etc we may or may not display as neccesary. It's current usage is not finalized and
# any suggestions for this are well taken
role Messenger {
has $.stdout = Supplier.new;
has $.stderr = Supplier.new;
}
enum LEVEL is export ;
enum STAGE is export ;
enum PHASE is export ;
# Get a resource located at a uri and save it to the local disk
role Fetcher {
method fetch($uri, $save-as) { ... }
method fetch-matcher($uri) { ... }
}
# As a post-hook to the default fetchers we will need to extract zip
# files. `git` does this itself, so a git based Fetcher wouldn't need this
# although we could possibly add `--no-checkout` to `git`s fetch and treat
# Extract as the action of `--checkout $branch` (allowing us to "extract"
# a specific version from a commit/tag)
role Extractor {
method extract($archive-file, $target-dir) { ... }
method ls-files($archive-file) { ... }
method extract-matcher($path) { ... }
}
# test a single file OR all the files in a directory (recursive optional)
role Tester {
method test($path, :@includes) { ... }
method test-matcher($path) { ... }
}
role Builder {
method build($dist, :@includes) { ... }
method build-matcher($path) { ... }
}
role Installer {
method install($dist, :$cur, :$force) { ... }
method install-matcher($dist) { ... }
}
role Reporter {
method report($dist) { ... }
}
role Candidate {
has $.dist;
has $.as; # Requested as (maybe a url, maybe an identity, maybe a path)
has $.from; # Recommended from (::Ecosystems, ::MetaCPAN, ::LocalCache)
has $.uri is rw; # url, file path, etc
has Bool $.is-dependency is rw;
has $.build-results is rw;
has $.test-results is rw;
}
role Repository {
# An identifier like .^name but intended to differentiate between instances of the same class
# For instance: ::Ecosystems and ::Ecosystems which would otherwise share the
# same .^name of ::Ecosystems
method id { $?CLASS.^name.split('+', 2)[0] }
# max-results is meant so we can :max-results(1) when we are interested in using it like
# `.candidates` (i.e. 1 match per identity) so we can stop iterating search plugins earlier
method search(:$max-results, *@identities, *%fields --> Iterable) { ... }
# Optional method currently being called after a search/fetch
# to assist ::Repository::LocalCache in updating its MANIFEST path cache.
# The concept needs more thought, but for instance a GitHub related repositories
# could commit changes or push to a remote branch, and (as is now) the cs
# ::LocalCache to update MANIFEST so we don't *have* to do a recursive folder search
#
# method store(*@dists) { }
# Optional method for listing available packages. For p6c style repositories
# where we have an index file this is easy. For metacpan style where we
# make a remote query not so much (maybe it could list the most recent X
# modules... or maybe it just doesn't implement it at all)
# method available { }
}
# Used by the phase's loader (i.e Zef::Fetch) to test that the plugin can
# be used. for instance, ::Shell wrappers probe via `cmd --help`. Note
# that the result of .probe is cached by each phase loader
role Probeable {
method probe returns Bool { ... }
}
role Pluggable {
has $!plugins;
has @.backends;
sub DEBUG($plugin, $message) {
say "[Plugin - {$plugin // $plugin // qq||}] $message"\
if ?%*ENV;
}
method plugins(*@names) {
+@names ?? self!list-plugins.grep({@names.contains(.short-name)}) !! self!list-plugins;
}
method !list-plugins {
gather for @!backends -> $plugin {
my $module = $plugin;
DEBUG($plugin, "Checking: {$module}");
# default to enabled unless `"enabled" : 0`
next() R, DEBUG($plugin, "\t(SKIP) Not enabled")\
if $plugin:exists && (!$plugin || $plugin eq "0");
next() R, DEBUG($plugin, "\t(SKIP) Plugin could not be loaded")\
if (try require ::($ = $module)) ~~ Nil;
DEBUG($plugin, "\t(OK) Plugin loaded successful");
if ::($ = $module).^find_method('probe') {
::($ = $module).probe
?? DEBUG($plugin, "\t(OK) Probing successful")
!! (next() R, DEBUG($plugin, "\t(SKIP) Probing failed"))
}
# add attribute `short-name` here to make filtering by name slightly easier
# until a more elegant solution can be integrated into plugins themselves
my $class = ::($ = $module).new(|($plugin // []))\
but role :: { has $.short-name = $plugin // '' };
next() R, DEBUG($plugin, "(SKIP) Plugin unusable: initialization failure")\
unless ?$class;
DEBUG($plugin, "(OK) Plugin is now usable: {$module}");
take $class;
}
}
}
zef-0.8.2/lib/Zef/ 0000775 0000000 0000000 00000000000 13557572301 0013607 5 ustar 00root root 0000000 0000000 zef-0.8.2/lib/Zef/Build.pm6 0000664 0000000 0000000 00000002724 13557572301 0015277 0 ustar 00root root 0000000 0000000 use Zef;
class Zef::Build does Pluggable {
submethod TWEAK(|) {
@ = self.plugins; # preload plugins
}
method build-matcher($dist) { self.plugins.grep(*.build-matcher($dist)) }
method build($candi, :@includes, Supplier :$logger, Int :$timeout, :$meta) {
my $dist := $candi.dist;
die "Can't build non-existent path: {$dist.path}" unless $dist.path.IO.e;
my $builder = self.build-matcher($dist).first(*.so);
die "No building backend available" unless ?$builder;
if ?$logger {
$logger.emit({ level => DEBUG, stage => BUILD, phase => START, candi => $candi, message => "Building with plugin: {$builder.^name}" });
$builder.stdout.Supply.grep(*.defined).act: -> $out { $logger.emit({ level => VERBOSE, stage => BUILD, phase => LIVE, candi => $candi, message => $out }) }
$builder.stderr.Supply.grep(*.defined).act: -> $err { $logger.emit({ level => ERROR, stage => BUILD, phase => LIVE, candi => $candi, message => $err }) }
}
my $todo = start { try $builder.build($dist, :@includes) };
my $time-up = ($timeout ?? Promise.in($timeout) !! Promise.new);
await Promise.anyof: $todo, $time-up;
$logger.emit({ level => DEBUG, stage => BUILD, phase => LIVE, candi => $candi, message => "Building {$dist.path} timed out" })
if ?$logger && $time-up.so && $todo.not;
my @got = $todo.so ?? $todo.result !! False;
@got;
}
}
zef-0.8.2/lib/Zef/CLI.pm6 0000664 0000000 0000000 00000132002 13557572301 0014640 0 ustar 00root root 0000000 0000000 use Zef;
use Zef::Client;
use Zef::Config;
use Zef::Utils::FileSystem;
use Zef::Identity;
use Zef::Distribution;
use Zef::Utils::SystemInfo;
use nqp;
# Content was cut+pasted from bin/zef, leaving bin/zef's contents as just: `use Zef::CLI;`
# This allows the bin/zef original code to be precompiled, halving bare start up time.
# Ideally this all ends up back in bin/zef once/if precompilation of scripts is handled in CURI
package Zef::CLI {
my $verbosity = preprocess-args-verbosity-mutate(@*ARGS);
%*ENV = $verbosity >= DEBUG;
my $CONFIG = preprocess-args-config-mutate(@*ARGS);
my $VERSION = try EVAL q[$?DISTRIBUTION.meta.first(*.so)];
# TODO: deprecate usage of --depsonly
@*ARGS = @*ARGS.map: { $_ eq '--depsonly' ?? '--deps-only' !! $_ }
proto MAIN(|) is export {
# Supress backtrace
CATCH { default { try { ::("Rakudo::Internals").?LL-EXCEPTION } ?? .rethrow !! .message.¬e; &*EXIT(1) } }
{*}
}
#| Download specific distributions
multi MAIN(
'fetch',
Bool :force(:$force-fetch),
Int :timeout(:$fetch-timeout) = %*ENV // 600,
Int :degree(:$fetch-degree) = %*ENV || 5, # default different from Zef::Client,
:$update,
*@identities ($, *@)
) {
my $client = get-client(:config($CONFIG), :$force-fetch, :$update, :$fetch-timeout, :$fetch-degree);
my @candidates = $client.find-candidates(@identities.map(*.&str2identity));
abort "Failed to resolve any candidates. No reason to proceed" unless +@candidates;
my @fetched = $client.fetch(@candidates);
my @fail = @candidates.grep: {.as !~~ any(@fetched>>.as)}
say "!!!> Fetch failed: {.as}{?($verbosity >= VERBOSE)??' at '~.dist.path!!''}" for @fail;
exit +@fetched && +@fetched == +@candidates && +@fail == 0 ?? 0 !! 1;
}
#| Run tests
multi MAIN(
'test',
Bool :force(:$force-test),
Int :timeout(:$test-timeout) = %*ENV || 3600,
# Int :degree(:$test-degree) = %*ENV || 1, # degree affects simutanious distributions being tests, but this tests a single distribution
*@paths ($, *@)
) {
my $client = get-client(:config($CONFIG), :$force-test, :$test-timeout);
my @candidates = $client.link-candidates( @paths.map(*.&path2candidate) );
abort "Failed to resolve any candidates. No reason to proceed" unless +@candidates;
my @tested = $client.test(@candidates);
my (:@test-pass, :@test-fail) := @tested.classify: {.test-results.grep(*.so) ?? !! }
say "!!!> Testing failed: {.as}{?($verbosity >= VERBOSE)??' at '~.dist.path!!''}" for @test-fail;
exit ?@test-fail ?? 1 !! ?@test-pass ?? 0 !! 255;
}
#| Run Build.pm
multi MAIN(
'build',
Bool :force(:$force-build),
Int :timeout(:$build-timeout) = %*ENV || 3600,
*@paths ($, *@)
) {
my $client = get-client(:config($CONFIG), :$force-build, |(:$build-timeout with $build-timeout),);
my @candidates = $client.link-candidates( @paths.map(*.&path2candidate) );
abort "Failed to resolve any candidates. No reason to proceed" unless +@candidates;
my @built = $client.build(@candidates);
my (:@pass, :@fail) := @built.classify: {.?build-results.grep(*.so).elems ?? !! }
say "!!!> Build failure: {.as}{?($verbosity >= VERBOSE)??' at '~.dist.path!!''}" for @fail;
exit ?@fail ?? 1 !! ?@pass ?? 0 !! 255;
}
#| Install
multi MAIN(
'install',
Bool :$fetch = True,
Bool :$build = True,
Bool :$test = True,
Bool :$depends = True,
Bool :$test-depends = $test,
Bool :$build-depends = $build,
Bool :$force,
Bool :$force-resolve = $force,
Bool :$force-fetch = $force,
Bool :$force-extract = $force,
Bool :$force-build = $force,
Bool :$force-test = $force,
Bool :$force-install = $force,
Int :$timeout,
Int :$fetch-timeout = %*ENV // $timeout // 600,
Int :$extract-timeout = %*ENV // $timeout // 3600,
Int :$build-timeout = %*ENV // $timeout // 3600,
Int :$test-timeout = %*ENV // $timeout // 3600,
Int :$install-timeout = %*ENV // $timeout // 3600,
Int :$degree,
Int :$fetch-degree = %*ENV || $degree || 5, # default different from Zef::Client
Int :$test-degree = %*ENV || $degree || 1,
Bool :$dry,
Bool :$upgrade,
Bool :$deps-only,
Bool :$serial,
Bool :$contained,
:$update,
:$exclude,
:to(:$install-to) = $CONFIG,
*@wants ($, *@)
) {
@wants .= map: *.&str2identity;
my (:@paths, :@uris, :@identities) := @wants.classify: -> $wanted {
$wanted ~~ /^[\. | \/]/ ??
!! ?Zef::Identity.new($wanted) ??
!! (my $uri = Zef::Utils::URI($wanted) and !$uri.is-relative) ??
!! abort("Don't understand identity: {$wanted}");
}
my $client = get-client(
:config($CONFIG), :$update, :exclude($exclude.map({ Zef::Distribution::DependencySpecification.new($_) })),
:$depends, :$test-depends, :$build-depends,
:$force-resolve, :$force-fetch, :$force-extract,
:$force-build, :$force-test, :$force-install,
:$fetch-timeout, :$extract-timeout, :$build-timeout,
:$test-timeout, :$install-timeout, :$fetch-degree,
:$test-degree,
);
# LOCAL PATHS
abort "The following were recognized as file paths but don't exist as such - {@paths.grep(!*.IO.e)}"
if +@paths.grep(!*.IO.e);
my (:@wanted-paths, :@skip-paths) := @paths\
.classify: {$client.is-installed(Zef::Distribution::Local.new($_).identity, :at($install-to.map(*.&str2cur))) ?? !! }
say "The following local path candidates are already installed: {@skip-paths.join(', ')}"\
if ($verbosity >= VERBOSE) && +@skip-paths;
my @requested-paths = ?$force-install ?? @paths !! @wanted-paths;
my @path-candidates = @requested-paths.map(*.&path2candidate);
# URIS
my @uri-candidates-to-check = $client.fetch( @uris.map({ Candidate.new(:as($_), :uri($_)) }) ) if +@uris;
abort "No candidates found matching uri: {@uri-candidates-to-check.join(', ')}" if +@uris && +@uri-candidates-to-check == 0;
my (:@wanted-uris, :@skip-uris) := @uri-candidates-to-check\
.classify: {$client.is-installed($_.dist.identity, :at($install-to.map(*.&str2cur))) ?? !! }
say "The following uri candidates are already installed: {@skip-uris.map(*.as).join(', ')}"\
if ($verbosity >= VERBOSE) && +@skip-uris;
my @requested-uris = (?$force-install ?? @uri-candidates-to-check !! @wanted-uris)\
.grep: { $_ ~~ none(@path-candidates.map(*.dist.identity)) }
my @uri-candidates = @requested-uris;
# IDENTITIES
my (:@wanted-identities, :@skip-identities) := @identities\
.classify: {$client.is-installed($_, :at($install-to.map(*.&str2cur))) ?? !! }
say "The following candidates are already installed: {@skip-identities.join(', ')}"\
if ($verbosity >= VERBOSE) && +@skip-identities;
my @requested-identities = (?$force-install ?? @identities !! @wanted-identities)\
.grep: { $_ ~~ none(@uri-candidates.map(*.dist.identity)) }
my @requested = $client.find-candidates(:$upgrade, @requested-identities) if +@requested-identities;
abort "No candidates found matching identity: {@requested-identities.join(', ')}"\
if +@requested-identities && +@requested == 0;
my @prereqs = $client.find-prereq-candidates(:skip-installed(not $contained), |@path-candidates, |@uri-candidates, |@requested)\
if +@path-candidates || +@uri-candidates || +@requested;
my @candidates = grep *.defined, ?$deps-only
?? @prereqs !! (|@path-candidates, |@uri-candidates, |@requested, |@prereqs);
unless +@candidates {
note("All candidates are currently installed");
exit(0) if $deps-only;
abort("No reason to proceed. Use --force-install to continue anyway", 0) unless $force-install;
}
my (:@local, :@remote) := @candidates.classify: {.dist ~~ Zef::Distribution::Local ?? !! }
my @fetched = grep *.so, |@local, ($client.fetch(@remote).Slip if +@remote && $fetch);
my CompUnit::Repository @to = $install-to.map(*.&str2cur);
my @installed = $client.make-install( :@to, :$fetch, :$test, :$build, :$upgrade, :$update, :$dry, :$serial, @fetched );
my @fail = @candidates.grep: {.as !~~ any(@installed>>.as)}
say "!!!> Install failures: {@fail.map(*.dist.identity).join(', ')}" if +@fail;
exit +@installed && +@installed == +@candidates && +@fail == 0 ?? 0 !! 1;
}
#| Uninstall
multi MAIN(
'uninstall',
:from(:$uninstall-from) = $CONFIG,
*@identities ($, *@)
) {
my $client = get-client(:config($CONFIG));
my CompUnit::Repository @from = $uninstall-from.map(*.&str2cur);
my @uninstalled = $client.uninstall( :@from, @identities.map(*.&str2identity) );
my @fail = @identities.grep(* !~~ any(@uninstalled.map(*.as)));
if +@uninstalled == 0 && +@fail {
note("!!!> Found no matching candidates to uninstall");
exit 1;
}
for @uninstalled.classify(*.from).kv -> $from, $candidates {
say "===> Uninstalled from $from";
say "$_" for |$candidates>>.dist>>.identity;
}
say "!!!> Failed to uninstall distributions: {@fail.join('. ')}" if +@fail;
exit +@fail ?? 1 !! 0;
}
#| Get a list of possible distribution candidates for the given terms
multi MAIN('search', Int :$wrap = False, :$update, *@terms ($, *@)) {
my $client = get-client(:config($CONFIG), :$update);
my @results = $client.search(@terms);
say "===> Found " ~ +@results ~ " results";
my @rows = eager gather for @results -> $candi {
FIRST { take [] }
take [ $++, $candi.from, $candi.dist.identity, ($candi.dist.hash // '') ];
}
print-table(@rows, :$wrap);
exit 0;
}
#| A list of available modules from enabled repositories
multi MAIN('list', Int :$max?, :$update, Bool :i(:$installed), *@at) {
my $client = get-client(:config($CONFIG), :$update);
my $found := ?$installed
?? $client.list-installed(@at.map(*.&str2cur))
!! $client.list-available(@at);
my $range := defined($max) ?? 0..+$max !! *;
my %locations = $found[$range].classify: -> $candi { $candi.from }
for %locations.kv -> $from, $candis {
note "===> Found via {$from}";
for $candis.sort(*.dist.identity) -> $candi {
say "{$candi.dist.identity}";
say "#\t{$_}" for @($candi.dist.provides.keys.sort if ?($verbosity >= VERBOSE));
}
}
exit 0;
}
#| Upgrade installed distributions (BETA)
multi MAIN(
'upgrade',
Bool :$fetch = True,
Bool :$build = True,
Bool :$test = True,
Bool :$depends = True,
Bool :$test-depends = $test,
Bool :$build-depends = $build,
Bool :$force,
Bool :$force-resolve = $force,
Bool :$force-fetch = $force,
Bool :$force-extract = $force,
Bool :$force-build = $force,
Bool :$force-test = $force,
Bool :$force-install = $force,
Int :$timeout,
Int :$fetch-timeout = %*ENV // $timeout // 600,
Int :$extract-timeout = %*ENV // $timeout // 3600,
Int :$build-timeout = %*ENV // $timeout // 3600,
Int :$test-timeout = %*ENV // $timeout // 3600,
Int :$install-timeout = %*ENV // $timeout // 3600,
Int :$degree,
Int :$fetch-degree = %*ENV || $degree || 5, # default different from Zef::Client,
Int :$test-degree = %*ENV || $degree || 1,
Bool :$dry,
Bool :$update,
Bool :$serial,
:$exclude,
:to(:$install-to) = $CONFIG,
*@identities
) {
# XXX: This is a very inefficient prototype. Not sure how to handle an 'upgrade' when
# multiple versions are already installed, so for now an 'upgrade' always means we
# leave the previous version installed.
my $client = get-client(
:config($CONFIG), :exclude($exclude.map({ Zef::Distribution::DependencySpecification.new($_) })),
:$depends, :$test-depends, :$build-depends,
:$force-resolve, :$force-fetch, :$force-extract,
:$force-build, :$force-test, :$force-install,
:$fetch-timeout, :$extract-timeout, :$build-timeout,
:$test-timeout, :$install-timeout, :$fetch-degree,
:$test-degree
);
my @missing = @identities.grep: { not $client.is-installed($_) };
abort "Can't upgrade identities that aren't installed: {@missing.join(', ')}" if +@missing;
my @installed = $client.list-installed($install-to.map(*.&str2cur))\
.sort(*.dist.ver).sort(*.dist.api).reverse\
.unique(:as({"{.dist.name}:auth<{.dist.auth-matcher}>"}));
my @requested = +@identities
?? $client.find-candidates(@identities.map(*.&str2identity))
!! $client.find-candidates(@installed.map(*.dist.clone(ver => "*")).map(*.identity).unique);
my (:@upgradable, :@current, :@unknown) := @requested.classify: -> $candi {
my $latest-installed = @installed.grep({ .dist.name eq $candi.dist.name })\
.sort({ .dist.auth-matcher ne $candi.dist.auth-matcher }).head; # this is to handle auths that changed. need to find a better way...
!$latest-installed ?? !! (($latest-installed.dist.ver <=> $candi.dist.ver) === Order::Less) ?? !! ;
}
note "Unsure of how to handle the following distributions: {@unknown.map(*.dist.identity),join(',')}" if +@unknown;
abort("All requested distributions are already at their latest versions", 0) unless +@upgradable;
say "The following distributions will be upgraded: {@upgradable.map(*.dist.identity).join(', ')}";
my &installer = &MAIN.assuming(
:$depends,
:$test-depends,
:$build-depends,
:$test,
:$fetch,
:$build,
:$update,
:$exclude,
:$install-to,
:$force-resolve,
:$force-fetch,
:$force-build,
:$force-test,
:$force-install,
:$fetch-timeout,
:$extract-timeout,
:$build-timeout,
:$test-timeout,
:$fetch-degree,
:$test-degree,
:$dry,
:$serial,
);
# Sort these ahead of time so they can be installed individually by passing
# the .uri instead of the identities (which would require another search)
my @sorted-candidates = $client.sort-candidates(@upgradable);
say "===> Updating: " ~ @sorted-candidates.map(*.dist.identity).join(', ');
my (:@upgraded, :@failed) := @sorted-candidates.map(*.uri).classify: -> $uri {
my &*EXIT = sub ($code) { return $code == 0 ?? True !! False };
try { &installer('install', $uri) } ?? !! ;
}
abort "!!!> Failed upgrading *all* modules" unless +@upgraded;
say "!!!> Some modules failed to update: {@failed.map(*.dist.identity).join(', ')}" if +@failed;
exit +@upgraded < +@upgradable ?? 1 !! 0;
}
#| View dependencies of a distribution
multi MAIN(
'depends',
$identity,
Bool :$depends = True,
Bool :$test-depends = True,
Bool :$build-depends = True,
) {
# TODO: refactor this stuff which was copied from 'install'
# So really we just need a function to handle separating the different identity types
# and optionally delivering a message for each section.
my @wants = ($identity,).map: *.&str2identity;
my (:@paths, :@uris, :@identities) := @wants.classify: -> $wanted {
$wanted ~~ /^[\. | \/]/ ??
!! ?Zef::Identity.new($wanted) ??
!! (my $uri = Zef::Utils::URI($wanted) and !$uri.is-relative) ??
!! abort("Don't understand identity: {$wanted}");
}
my $client = Zef::Client.new(:config($CONFIG), :$depends, :$test-depends, :$build-depends,);
abort "The following were recognized as file paths but don't exist as such - {@paths.grep(!*.IO.e)}"
if +@paths.grep(!*.IO.e);
my @path-candidates = @paths.map(*.&path2candidate);
my @uri-candidates-to-check = $client.fetch( @uris.map({ Candidate.new(:as($_), :uri($_)) }) ) if +@uris;
abort "No candidates found matching uri: {@uri-candidates-to-check.join(', ')}" if +@uris && +@uri-candidates-to-check == 0;
my @uri-candidates = @uri-candidates-to-check.grep: { $_ ~~ none(@path-candidates.map(*.dist.identity)) }
my @requested-identities = @identities.grep: { $_ ~~ none(@uri-candidates.map(*.dist.identity)) }
my @requested = $client.find-candidates(@requested-identities) if +@requested-identities;
abort "No candidates found matching identity: {@requested-identities.join(', ')}"\
if +@requested-identities && +@requested == 0;
my @prereqs = $client.find-prereq-candidates(:!skip-installed, |@path-candidates, |@uri-candidates, |@requested)\
if +@path-candidates || +@uri-candidates || +@requested;
.say for @prereqs.map(*.dist.identity);
}
#| View direct reverse dependencies of a distribution
multi MAIN(
'rdepends',
$identity,
Bool :$depends = True,
Bool :$test-depends = True,
Bool :$build-depends = True,
) {
my $client = get-client(:config($CONFIG), :$depends, :$test-depends, :$build-depends);
.dist.identity.say for $client.list-rev-depends($identity);
exit 0;
}
#| Lookup locally installed distributions by short-name, name-path, or sha1 id
multi MAIN('locate', $identity, Bool :$sha1) {
my $client = get-client(:config($CONFIG));
if !$sha1 {
if $identity.ends-with('.pm' | '.pm6' | '.rakumod') {
my @candis = $client.list-installed.grep({
.dist.compat.meta.values.grep({parse-value($_) eq $identity}).so;
});
for @candis -> $candi {
LAST exit 0;
NEXT say '';
if $candi {
# This is relying on implementation details for compatability purposes. It will
# use something more appropriate sometime in 2019.
my %meta = $candi.dist.compat.meta;
%meta = %meta.map({ $_.key => parse-value($_.value) }).hash;
my $lib = %meta.hash.antipairs.hash.{$identity};
my $lib-sha1 = nqp::sha1($lib ~ CompUnit::Repository::Distribution.new($candi.dist.compat).id);
say "===> From Distribution: {~$candi.dist}";
say "{$lib} => {$candi.from.prefix.child('sources').child($lib-sha1)}";
}
}
}
elsif $identity.starts-with('bin/' | 'resources/') {
my @candis = $client.list-installed.grep({
.dist.compat.meta.first({.key eq $identity}).so
});
for @candis -> $candi {
LAST exit 0;
NEXT say '';
if $candi {
my $libs = $candi.dist.compat.meta;
my $lib = $libs.first({.key eq $identity});
say "===> From Distribution: {~$candi.dist}";
say "{$identity} => {$candi.from.prefix.child('resources').child($lib.value)}";
}
}
}
elsif $client.resolve($identity) -> @candis {
for @candis -> $candi {
LAST exit 0;
NEXT say '';
say "===> From Distribution: {~$candi.dist}";
my $source-prefix = $candi.from.prefix.child('sources');
my $source-path = $source-prefix.child(nqp::sha1($identity ~ CompUnit::Repository::Distribution.new($candi.dist.compat).id));
say "{$identity} => {$source-path}" if $source-path.IO.f;
}
}
}
else {
my @candis = $client.list-installed.grep(-> $candi {
# This is relying on implementation details for compatability purposes. It will
# use something more appropriate sometime in 2019.
use nqp;
my %meta = $candi.dist.compat.meta;
%meta = %meta.map({ $_.key => parse-value($_.value) }).hash;
my @source_files = %meta.map({ nqp::sha1($_.key ~ CompUnit::Repository::Distribution.new($candi.dist.compat).id) });
my @resource_files = %meta.values.first({$_ eq $identity});
$identity ~~ any(grep *.defined, flat @source_files, @resource_files);
});
for @candis -> $candi {
LAST exit 0;
NEXT say '';
if $candi {
my %meta = $candi.dist.compat.meta;
%meta = %meta.map({ $_.key => parse-value($_.value) }).hash;
my %sources = %meta.map({ $_.key => nqp::sha1($_.key ~ CompUnit::Repository::Distribution.new($candi.dist.compat).id) }).hash;
say "===> From Distribution: {~$candi.dist}";
$identity ~~ any(%sources.values)
?? (say "{$_} => {$candi.from.prefix.child('sources').child($identity)}" for %sources.antipairs.hash{$identity})
!! (say "{.key} => {.value}" for $candi.dist.compat.meta.first({.value eq $identity}));
}
}
}
say "!!!> Nothing located";
exit 1;
}
#| Detailed distribution information
multi MAIN('info', $identity, :$update, Int :$wrap = False) {
my $client = get-client(:config($CONFIG), :$update);
my $latest-installed-candi = $client.resolve($identity).head;
my @remote-candis = $client.search($identity, :strict, :max-results(1));
abort "!!!> Found no candidates matching identity: {$identity}"
unless $latest-installed-candi || +@remote-candis;
my $candi := ($latest-installed-candi, |@remote-candis).grep(*.defined).sort(*.dist.ver).sort(*.dist.api).tail;
my $dist := $candi.dist;
say "- Info for: $identity";
say "- Identity: {$dist.identity}";
say "- Recommended By: {$candi.from}";
say "- Installed: {$latest-installed-candi??$latest-installed-candi.dist.identity eq $dist.identity??qq|Yes|!!qq|Yes, as $latest-installed-candi.dist.identity()|!!'No'}";
say "Author:\t {$dist.author}" if $dist.author;
say "Description:\t {$dist.description}" if $dist.description;
say "License:\t {$dist.compat.meta}" if $dist.compat.meta;
say "Source-url:\t {$dist.source-url}" if $dist.source-url;
my @provides = $dist.provides.sort(*.key.chars);
say "Provides: {@provides.elems} modules";
if ?($verbosity >= VERBOSE) {
my $meta := $dist.compat.meta;
my @rows = eager gather for @provides -> $lib {
FIRST {
take []
}
my $module-name = $lib.key;
my $name-path = parse-value($lib.value);
take [ $module-name, $name-path ];
}
print-table(@rows, :$wrap);
}
if $dist.hash {
say "Support:";
for $dist.hash.kv -> $k, $v {
say "# $k:\t$v";
}
}
my @deps = (|$dist.depends-specs, |$dist.test-depends-specs, |$dist.build-depends-specs).grep(*.defined).unique;
say "Depends: {@deps.elems} items";
if ?($verbosity >= VERBOSE) {
my @rows = eager gather for @deps -> $spec {
FIRST { take [] }
my $row = [ "{state $id += 1}", $spec.name, ($client.is-installed($spec) ?? '✓' !! '')];
take $row;
}
print-table(@rows, :$wrap);
}
exit 0;
}
#| Browse a distribution's available support urls (homepage, bugtracker, source)
multi MAIN('browse', $identity, $url-type where * ~~ any(), Bool :$open = True) {
my $client = get-client(:config($CONFIG));
my $candi = $client.resolve($identity).head
|| $client.search($identity, :strict, :max-results(1))[0]\
|| abort "!!!> Found no candidates matching identity: {$identity}";
my %support = $candi.dist.compat.meta;
my $url = %support{$url-type};
my @has-urls = grep { %support{$_} }, ;
unless $url && $url.starts-with('http://' | 'https://') {
say "'browse' urls supported by $identity: {+@has-urls??@has-urls.join(',')!!'none'}";
exit 255;
}
say $url;
my @cmd = $*DISTRO.is-win ??
!! $*VM.osname eq 'darwin' ??
!! ;
run( |@cmd, $url ) if $open;
}
#| Download a single module and change into its directory
multi MAIN('look', $identity) {
my $client = get-client(:config($CONFIG));
my @candidates = $client.find-candidates( str2identity($identity) );
abort "Failed to resolve any candidates. No reason to proceed" unless +@candidates;
my (:@remote, :@local) := @candidates.classify: {.dist !~~ Zef::Distribution::Local ?? !! }
my $fetched = @local[0] || $client.fetch(@remote[0])[0] || abort "Failed to fetch candidate: $identity";
my $dist-path = $fetched.dist.path;
say "===> Shelling into directory: {$dist-path}";
exit so shell(%*ENV // %*ENV // %*ENV, :cwd($dist-path)) ?? 0 !! 1;
}
#| Smoke test
multi MAIN(
'smoke',
Bool :$fetch = True,
Bool :$build = True,
Bool :$test = True,
Bool :$depends = True,
Bool :$test-depends = $test,
Bool :$build-depends = $build,
Bool :$force,
Bool :$force-resolve = $force,
Bool :$force-fetch = $force,
Bool :$force-extract = $force,
Bool :$force-build = $force,
Bool :$force-test = $force,
Bool :$force-install = $force,
Int :$timeout,
Int :$fetch-timeout = %*ENV // $timeout // 600,
Int :$extract-timeout = %*ENV // $timeout // 3600,
Int :$build-timeout = %*ENV // $timeout // 3600,
Int :$test-timeout = %*ENV // $timeout // 3600,
Int :$install-timeout = %*ENV // $timeout // 3600,
Int :$degree,
Int :$fetch-degree = %*ENV || $degree || 5, # default different from Zef::Client,
Int :$test-degree = %*ENV || $degree || 1,
Bool :$update,
Bool :$upgrade,
Bool :$dry,
Bool :$serial,
:$exclude,
:to(:$install-to) = $CONFIG,
) {
my $client = get-client(
:config($CONFIG), :exclude($exclude.map({ Zef::Distribution::DependencySpecification.new($_) })),
:$depends, :$test-depends, :$build-depends,
:$force-resolve, :$force-fetch, :$force-extract,
:$force-build, :$force-test, :$force-install,
:$fetch-timeout, :$extract-timeout, :$build-timeout,
:$test-timeout, :$install-timeout, :$fetch-degree,
:$test-degree,
);
my @identities = $client.list-available.map(*.dist.identity).unique;
my CompUnit::Repository @to = $install-to.map(*.&str2cur);
say "===> Smoke testing with {+@identities} distributions...";
my &installer = &MAIN.assuming(
'install',
:$depends,
:$test-depends,
:$build-depends,
:$test,
:$fetch,
:$build,
:$update,
:$upgrade,
:$exclude,
:$install-to,
:$force-resolve,
:$force-fetch,
:$force-build,
:$force-test,
:$force-install,
:$fetch-timeout,
:$extract-timeout,
:$build-timeout,
:$test-timeout,
:$fetch-degree,
:$test-degree,
:$dry,
:$serial,
);
for @identities -> $identity {
my &*EXIT = sub ($code) { return $code == 0 ?? True !! False };
my $result = try installer($identity);
say "===> Smoke result for {$identity}: {?$result??'OK'!!'NOT OK'}";
}
exit 0;
}
#| Update package indexes
multi MAIN('update', *@names) {
my $client = get-client(:config($CONFIG));
my %results = $client.recommendation-manager.update(@names);
my $rows = %results.map: {[.key, .value]};
abort "A plugin name was provided that does not exist or does not support 'update'"
if +@names && (+@names > +$rows);
print-table( [["Content Storage", "Distribution Count"], |$rows], wrap => True );
exit 0;
}
#| Nuke module installations (site, home) and repositories from config (RootDir, StoreDir, TempDir)
multi MAIN('nuke', Bool :$confirm, *@names ($, *@)) {
my sub dir-delete($dir) {
my @deleted = grep *.defined, try delete-paths($dir, :f, :d, :r);
say "Deleted " ~ +@deleted ~ " paths from $dir/*";
}
my sub confirm-delete(*@dirs) {
for @dirs -> $dir {
next() R, say "$dir does not exist. Skipping..." unless $dir.IO.e;
given prompt("Delete {$dir.path}/* [y/n]: ") {
when any() { dir-delete($dir) }
when any() { say "Skipping..." }
default { say "Invalid entry (enter Y or N)"; redo }
}
}
}
my @config-keys = ;
my @config-dirs = $CONFIG<<{@names (&) @config-keys}>>.map(*.IO.absolute).sort;
my @curli-dirs = @names\
.grep(* !~~ any(@config-keys))\
.map(*.&str2cur)\
.grep(*.?can-install)\
.map(*.prefix.absolute);
my @delete = |@curli-dirs, |@config-dirs;
$confirm === False ?? @delete.map(*.&dir-delete) !! confirm-delete( @delete );
exit 0;
}
#| Detailed version information
multi MAIN(Bool :$version where .so) {
say $*PERL.compiler.version <= v2018.12
?? 'Version detection requires a rakudo newer than v2018.12'
!! ($VERSION // 'unknown');
exit 0;
}
multi MAIN(Bool :h(:$help)?) {
note qq:to/END_USAGE/
Zef - Perl6 Module Management
USAGE
zef [flags|options] command [args]
zef --version
COMMANDS
install Install specific dependencies by name or path
uninstall Uninstall specified distributions
test Run tests on a given module's path
fetch Fetch and extract module's source
build Run the Build.pm in a given module's path
look Fetch followed by shelling into the module's path
update Update package indexes for repositories
upgrade (BETA) Upgrade specific distributions (or all if no arguments)
search Show a list of possible distribution candidates for the given terms
info Show detailed distribution information
browse Open browser to various support urls (homepage, bugtracker, source)
list List known available distributions, or installed distributions with `--installed`
depends List all direct and transitive dependencies for a given identity
rdepends List all distributions directly depending on a given identity
locate Lookup installed module information by short-name, name-path, or sha1 (with --sha1 flag)
smoke Run smoke testing on available modules
nuke Delete directory/prefix containing matching configuration path or CURLI name
OPTIONS
--install-to=[name] Short name or spec of CompUnit::Repository to install to
--config-path=[path] Load a specific Zef config file
--[phase]-timeout=[int] Set a timeout (in seconds) for the corresponding phase ( phase: fetch, extract, build, test, install )
--[phase]-degree=[int] Number of simultaneous distributions/jobs to process for the corresponding phase ( phase : fetch, test )
--update Force a refresh for all module indexes
--update=[ecosystem] Force a refresh for a specific ecosystem module index
--/update Skip refreshing all module indexes
--/update=[ecosystem] Skip refreshing for a specific ecosystem module index
ENV OPTIONS
ZEF_[phase]_TIMEOUT See --[phase]-timeout ( phases: FETCH, BUILD, TEST, INSTALL )
ZEF_[phase]_DEGREE See --[phase]-degree ( phases: FETCH, TEST )
VERBOSITY LEVEL (from least to most verbose)
--error, --warn, --info (default), --verbose, --debug
FLAGS
--deps-only Install only the dependency chains of the requested distributions
--dry Run all phases except the actual installations
--serial Install each dependency after passing testing and before building/testing the next dependency
--contained (BETA) Install all transitive and direct dependencies regardless if they are already installed globally
--/test Skip the testing phase
--/build Skip the building phase
--/depends Do not fetch runtime dependencies
--/test-depends Do not fetch test dependencies
--/build-depends Do not fetch build dependencies
FORCE FLAGS
Ignore errors occuring during the corresponding phase:
--force-resolve --force-fetch --force-extract --force-build --force-test --force-install
CONFIGURATION {$CONFIG.IO.absolute}
Enable or disable plugins that match the configuration that has field `short-name` that matches
-- # `--cpan` Enable plugin with short-name `cpan`
--/ # `--/cpan` Disable plugin with short-name `cpan`
END_USAGE
}
proto sub abort(|) {*}
multi sub abort(Int $exit-code, Str $str) { samewith($str, $exit-code) }
multi sub abort(Str $str, Int $exit-code = 255) { say $str; exit $exit-code }
# Filter/mutate out verbosity flags from @*ARGS and return a verbosity level
sub preprocess-args-verbosity-mutate(*@_) {
my (:@log-level, :@filtered-args) := @_.classify: {
$_ ~~ any(<--fatal --error --warn --info -v --verbose --debug --trace>)
??
!! ;
}
@*ARGS = @filtered-args;
do given any(@log-level) {
when '--fatal' { FATAL }
when '--error' { ERROR }
when '--warn' { WARN }
when '--info' { INFO }
when '--verbose' { VERBOSE }
when '-v' { VERBOSE }
when '--debug' { DEBUG }
when '--trace' { TRACE }
default { INFO }
}
}
# Second crack at cli config modification
# Currently only uses Bools `--name` and `--/name` to enable and disable a plugin
# Note that `name` can match the config plugin key `short-name` or `module`
# * Now also removes --config-path $path parameters
# TODO: Turn this into a more general getopts
sub preprocess-args-config-mutate(*@args) {
# get/remove --config-path=xxx
# MUTATES @*ARGS
my Str $config-path-from-args;
for |@args.flatmap(*.split(/\=/, 2)).rotor(2 => -1, :partial) {
$config-path-from-args = ~$_[1] if $_[0] eq '--config-path' && $_[1];
LAST {
@*ARGS = eager gather for |@args.kv -> $key, $value {
take($value) unless $value.starts-with('--config-path')
|| ($key > 0 && @args[$key - 1] eq '--config-path')
}
}
}
my $chosen-config-file = $config-path-from-args // Zef::Config::guess-path();
# Keep track of the original path so we can show it on the --help usage :-/
my $config = do {
# The .Str.IO thing is due to a weird rakudo bug I can't figure out .
# A bare .IO will complain that its being called on a type Any (not true)
my $path = $config-path-from-args // Zef::Config::guess-path;
my $IO = $path.Str.IO;
my %hash = Zef::Config::parse-file($path).hash;
class :: {
has $.IO;
has %.hash handles ;
}.new(:%hash, :$IO);
}
# - Move named options to start of @*ARGS so the git familiar style of options after positionals works
# - get/remove --$short-name and --/$short-name where $short-name is a value in the config file
my $plugin-lookup := Zef::Config::plugin-lookup($config.hash);
for @*ARGS -> $arg {
state @positional;
state @named;
LAST { @*ARGS = flat @named, @positional; }
my $arg-as = $arg.subst(/^["--" | "--\/"]/, '');
my $enabled = $arg.starts-with('--/') ?? 0 !! 1;
$arg.starts-with('-')
?? $arg-as ~~ any($plugin-lookup.keys)
?? (for |$plugin-lookup{$arg-as} -> $p { $p = $enabled })
!! @named.append($arg)
!! @positional.append($arg);
}
$config;
}
sub get-client(*%_) {
my $client = Zef::Client.new(|%_);
my $logger = $client.logger;
my $stdout = $logger.Supply.grep({ . <= $verbosity });
my $reporter = $logger.Supply.grep({
(. == TEST && . == AFTER)
|| (. == ERROR && . == AFTER)
|| (. == FATAL && . == AFTER)
});
$stdout.tap: -> $m {
given $m. {
when BEFORE { say "===> {$m.}" }
when AFTER { say "===> {$m.}" }
default {
# Prefix output with a name that references its source since
# lines may be coming from many sources at once.
my $line-prefix = ((.dist??.dist.meta!!Nil) // .as) with $m.;
say($line-prefix ?? "[$line-prefix] $_" !! $_) for $m..lines;
}
}
}
$reporter.tap: -> $event {
$client.reporter.report($event, :$logger);
};
if %_.defined {
my @plugins = $client.recommendation-manager.plugins;
if %_ === Bool::False {
@plugins.map({ try .auto-update = False });
}
elsif %_ === Bool::True {
@plugins.map(*.?update);
}
else {
@plugins.grep({.short-name ~~ any(%_.grep(*.not))}).map({ try .auto-update = False });
@plugins.grep({.short-name ~~ any(%_.grep(*.so))}).map(*.?update);
}
}
$client;
}
# maybe its a name, maybe its a spec/path. either way Zef::App methods take a CURs, not strings
sub str2cur($target) {
my $named-repo = CompUnit::RepositoryRegistry.repository-for-name($target);
return $named-repo if $named-repo;
# first try 'site', then try 'home'
if $target eq 'auto' {
state $cur =
first { .can-install() },
map { CompUnit::RepositoryRegistry.repository-for-name($_) },
;
return $cur if $cur;
}
# Technically a path without any short-id# is a CURFS, but now it needs to be explicitly declared file#
# so that the more common case can be used without the prefix (inst#). This only applies when the path
# exists, so that short-names (site, home) that don't exist still throw errors instead of creating a directory.
my $spec-target = $target ~~ m/^\w+\#.*?[\. | \/]/
?? $target
!! $target.IO.e
?? "inst#{$target}"
!! $target;
return CompUnit::RepositoryRegistry.repository-for-spec(~$spec-target, :next-repo($*REPO));
}
sub path2candidate($path) {
Candidate.new(
as => $path,
uri => $path.IO.absolute,
dist => Zef::Distribution::Local.new($path),
)
}
# prints a table with rows and columns. expects a header row.
# automatically adjusts column widths, as well as `yada`ing
# any characters on a line past $max-width
sub print-table(@rows, Int :$wrap) {
# this ugly thing is so users can pass in Bool or Int as a MAIN argument
my $max-width = ($*OUT.t && $wrap.perl eq 'Bool::False')
?? GET-TERM-COLUMNS()
!! $wrap.perl eq 'Bool::True'
?? 0
!! $wrap;
# returns formatted row
my sub _row2str (@widths, @cells, Int :$max) {
my $format = @widths.map({"%-{$_}s"}).join('|');
my $str = sprintf( $format, @cells.map({ $_ // '' }) );
return $str unless ?$max && $str.chars > $max;
my $cutoff = $str.substr(0, $max || $str.chars);
return $cutoff unless $cutoff.chars > 3;
return ($cutoff.substr(0,*-3) ~ '...') if $cutoff.substr(*-3,3) ~~ /\S\S\S/;
return ($cutoff.substr(0,*-2) ~ '..') if $cutoff.substr(*-2,2) ~~ /\S\S/;
return ($cutoff.substr(0,*-1) ~ '.') if $cutoff.substr(*-1,1) ~~ /\S/;
return $cutoff;
}
# Iterate over ([1,2,3],[2,3,4,5],[33,4,3,2]) to find the longest string in each column
my sub _get_column_widths ( *@rows ) {
return @rows[0].keys.map: { @rows>>[$_]>>.chars.max }
}
my @widths = _get_column_widths(@rows);
my @fixed-rows = @rows.map: { _row2str(@widths, @$_, :max($max-width)) }
if +@fixed-rows {
my $width = [+] _get_column_widths(@fixed-rows);
my $sep = '-' x $width;
say "{$sep}\n{@fixed-rows[0]}\n{$sep}";
.say for @fixed-rows[1..*];
say $sep;
}
}
sub parse-value($str-or-kv) {
do given $str-or-kv {
when Str { $_ }
when Hash { $_.keys[0] }
when Pair { $_.key }
}
}
}
zef-0.8.2/lib/Zef/Client.pm6 0000664 0000000 0000000 00000103530 13557572301 0015453 0 ustar 00root root 0000000 0000000 use Zef;
use Zef::Distribution;
use Zef::Distribution::Local;
use Zef::Distribution::DependencySpecification;
use Zef::Repository;
use Zef::Utils::FileSystem;
use Zef::Fetch;
use Zef::Extract;
use Zef::Build;
use Zef::Test;
use Zef::Install;
use Zef::Report;
class Zef::Client {
has $.cache;
has $.indexer;
has $.fetcher;
has $.recommendation-manager;
has $.extractor;
has $.tester;
has $.builder;
has $.installer;
has $.reporter;
has $.config;
has $.logger = Supplier.new;
has @.exclude; # user supplied
has @!ignore; # internal use
has Bool $.force-resolve is rw = False;
has Bool $.force-fetch is rw = False;
has Bool $.force-extract is rw = False;
has Bool $.force-build is rw = False;
has Bool $.force-test is rw = False;
has Bool $.force-install is rw = False;
has Int $.fetch-degree is rw = 1;
has Int $.test-degree is rw = 1;
has Int $.fetch-timeout is rw = 600;
has Int $.extract-timeout is rw = 3600;
has Int $.build-timeout is rw = 3600;
has Int $.test-timeout is rw = 3600;
has Int $.install-timeout is rw = 3600;
has Bool $.depends is rw = True;
has Bool $.build-depends is rw = True;
has Bool $.test-depends is rw = True;
submethod TWEAK(
:$!cache = $!config,
:$!fetcher = Zef::Fetch.new(:backends(|$!config)),
:$!extractor = Zef::Extract.new(:backends(|$!config)),
:$!builder = Zef::Build.new(:backends(|$!config)),
:$!installer = Zef::Install.new(:backends(|$!config)),
:$!tester = Zef::Test.new(:backends(|$!config)),
:$!reporter = Zef::Report.new(:backends(|$!config)),
:$!recommendation-manager = Zef::Repository.new(:backends($!config.map({ $_ = $!cache; $_ = $!fetcher; $_ }).Slip)),
) {
mkdir $!cache unless $!cache.IO.e;
@!ignore = \
.map({ Zef::Distribution::DependencySpecification.new($_) });
}
method find-candidates(Bool :$upgrade, *@identities ($, *@)) {
self.logger.emit({
level => INFO,
stage => RESOLVE,
phase => BEFORE,
message => "Searching for: {@identities.join(', ')}",
});
my @candidates = self!find-candidates(:$upgrade, @identities);
for @candidates.classify({.from}).kv -> $from, $found {
self.logger.emit({
level => VERBOSE,
stage => RESOLVE,
phase => AFTER,
message => "Found: {$found.map(*.dist.identity).join(', ')} [via {$from}]",
})
}
return @candidates;
}
method !find-candidates(Bool :$upgrade, *@identities ($, *@)) {
my $candidates := $!recommendation-manager.candidates(@identities, :$upgrade)\
.grep(-> $candi { not @!exclude.first({$candi.dist.contains-spec($_)}) })\
.grep(-> $candi { not @!ignore.first({$candi.dist.contains-spec($_)}) })\
.unique(:as(*.dist.identity));
}
method find-prereq-candidates(Bool :$skip-installed = True, Bool :$upgrade, *@candis ($, *@)) {
my @skip = @candis.map(*.dist);
my $prereqs := gather {
my @specs = self.list-dependencies(@candis);
while @specs.splice -> @specs-batch {
self.logger.emit({
level => DEBUG,
stage => RESOLVE,
phase => BEFORE,
message => "Dependencies: {@specs-batch.map(*.name).unique.join(', ')}",
});
next unless my @needed = @specs-batch\ # The current set of specs
.grep({ not @skip.first(*.contains-spec($_)) })\ # Dists in @skip are not needed
.grep(-> $spec { not @!exclude.first({ $_.spec-matcher($spec) }) })\
.grep(-> $spec { not @!ignore.first({ $_.spec-matcher($spec) }) })\
.grep({ $skip-installed ?? self.is-installed($_).not !! True });
my @identities = @needed.map(*.identity);
self.logger.emit({
level => INFO,
stage => RESOLVE,
phase => BEFORE,
message => "Searching for missing dependencies: {@identities.join(', ')}",
});
my @prereq-candidates = self!find-candidates(:$upgrade, @identities);
my $not-found := @needed.grep({ not @prereq-candidates.first(*.dist.contains-spec($_)) }).map(*.identity);
# The failing part of this should ideally be handled in Zef::CLI I think
if +@prereq-candidates == +@needed || $not-found.cache.elems == 0 {
for @prereq-candidates.classify({.from}).kv -> $from, $found {
self.logger.emit({
level => VERBOSE,
stage => RESOLVE,
phase => AFTER,
message => "Found dependencies: {$found.map(*.dist.identity).join(', ')} [via {$from}]",
})
}
}
else {
self.logger.emit({
level => ERROR,
stage => RESOLVE,
phase => AFTER,
message => "Failed to find dependencies: {$not-found.join(', ')}",
});
$!force-resolve
?? $!logger.emit({
level => ERROR,
stage => RESOLVE,
phase => LIVE,
message => 'Failed to resolve missing dependencies, but continuing with --force-resolve',
})
!! die('Failed to resolve some missing dependencies');
};
@skip.append: @prereq-candidates.map(*.dist);
@specs = self.list-dependencies(@prereq-candidates);
for @prereq-candidates -> $prereq {
$prereq.is-dependency = True;
take $prereq;
}
}
}
$prereqs.unique(:as(*.dist.identity));
}
method fetch(*@candidates ($, *@)) {
my @fetched = self!fetch(@candidates);
my @extracted = self!extract(@candidates);
my @local-candis = @extracted.map: -> $candi {
my $dist = Zef::Distribution::Local.new(~$candi.uri);
$candi.clone(:$dist);
}
$!recommendation-manager.store(@local-candis.map(*.dist));
@local-candis;
}
method !fetch(*@candidates ($, *@)) {
my $dispatcher := $*PERL.compiler.version < v2018.08
?? @candidates
!! @candidates.hyper(:batch(1), :degree($!fetch-degree || 5));
my @fetched = $dispatcher.map: -> $candi {
self.logger.emit({
level => DEBUG,
stage => FETCH,
phase => BEFORE,
message => "Fetching: {$candi.as}",
});
die "Cannot determine a uri to fetch {$candi.as} from. Perhaps it's META6.json is missing an e.g. source-url"
unless $candi.uri;
my $tmp = $!config.IO.child("{time}.{$*PID}.{(^10000).rand}");
my $stage-at = $tmp.child($candi.uri.IO.basename);
die "failed to create directory: {$tmp.absolute}"
unless ($tmp.IO.e || mkdir($tmp));
# $candi.uri will always point to where $candi.dist should be copied from.
# It could be a file or url; $dist.source-url contains where the source was
# originally located but we may want to use a local copy (while retaining
# the original source-url for some other purpose like updating)
my $save-to = $!fetcher.fetch($candi, $stage-at, :$!logger, :timeout($!fetch-timeout));
my $relpath = $stage-at.relative($tmp);
my $extract-to = $!cache.IO.child($relpath);
if !$save-to {
self.logger.emit({
level => ERROR,
stage => FETCH,
phase => AFTER,
message => "Fetching [FAIL]: {$candi.dist.?identity // $candi.as} from {$candi.uri}",
});
$!force-fetch
?? $!logger.emit({
level => ERROR,
stage => FETCH,
phase => LIVE,
candi => $candi,
message => 'Failed to fetch, but continuing with --force-fetch',
})
!! die("Aborting due to fetch failure: {$candi.dist.?identity // $candi.uri} (use --force-fetch to override)");
}
else {
self.logger.emit({
level => VERBOSE,
stage => FETCH,
phase => AFTER,
message => "Fetching [OK]: {$candi.dist.?identity // $candi.as} to $save-to",
});
}
$candi.uri = $save-to;
$candi;
};
return @fetched;
}
method !extract(*@candidates ($, *@)) {
my @extracted = eager gather for @candidates -> $candi {
self.logger.emit({
level => DEBUG,
stage => EXTRACT,
phase => BEFORE,
message => "Extracting: {$candi.as}",
});
my $tmp = $candi.uri.parent;
my $stage-at = $candi.uri;
my $relpath = $stage-at.relative($tmp);
my $extract-to = $!cache.IO.child($relpath);
die "failed to create directory: {$tmp.absolute}"
unless ($tmp.IO.e || mkdir($tmp));
my $meta6-prefix = '' R// $!extractor.ls-files($candi).sort.first({ .IO.basename eq 'META6.json' });
self.logger.emit({
level => WARN,
stage => EXTRACT,
phase => BEFORE,
message => "Extraction: Failed to find a META6.json file for {$candi.dist.?identity // $candi.as} -- failure is likely",
}) unless $meta6-prefix;
my $extracted-to = $!extractor.extract($candi, $extract-to, :$!logger, :timeout($!extract-timeout));
if !$extracted-to {
self.logger.emit({
level => ERROR,
stage => EXTRACT,
phase => AFTER,
message => "Extraction [FAIL]: {$candi.dist.?identity // $candi.as} from {$candi.uri}",
});
$!force-extract
?? $!logger.emit({
level => ERROR,
stage => EXTRACT,
phase => LIVE,
candi => $candi,
message => 'Failed to extract, but continuing with --force-extract',
})
!! die("Aborting due to extract failure: {$candi.dist.?identity // $candi.uri} (use --force-extract to override)");
}
else {
try { delete-paths($tmp) }
# Remove this when META.info support can finally be removed
if !$meta6-prefix and my $meta-info = $extracted-to.IO.add('META.info') and $meta-info.e {
self.logger.emit({
level => WARN,
stage => EXTRACT,
phase => AFTER,
message => "Extraction: Failed to find a META6.json file for {$candi.dist.?identity // $candi.as} -- creating it from deprecated META.info file",
});
try { $meta-info.copy($meta-info.parent.add('META6.json')) }
}
self.logger.emit({
level => VERBOSE,
stage => EXTRACT,
phase => AFTER,
message => "Extraction [OK]: {$candi.as} to {$extract-to}",
});
}
$candi.uri = $extracted-to.child($meta6-prefix);
take $candi;
}
}
# xxx: needs some love. also an entire specification
method build(*@candidates ($, *@)) {
my @built = eager gather for @candidates -> $candi {
my $dist := $candi.dist;
unless $!builder.build-matcher($dist) {
self.logger.emit({
level => DEBUG,
stage => BUILD,
phase => BEFORE,
message => "# SKIP: No need to build {$candi.dist.?identity // $candi.as}",
});
take $candi;
next();
}
$!logger.emit({
level => INFO,
stage => BUILD,
phase => BEFORE,
message => "Building: {$candi.dist.?identity // $candi.as}",
});
my $result := $!builder.build($candi, :includes($candi.dist.metainfo // []), :$!logger, :timeout($!build-timeout)).cache;
$candi.build-results = $result;
if $result.grep(*.not).elems {
self.logger.emit({
level => ERROR,
stage => BUILD,
phase => AFTER,
message => "Building [FAIL]: {$candi.dist.?identity // $candi.as}",
});
$!force-build
?? $!logger.emit({
level => ERROR,
stage => BUILD,
phase => LIVE,
candi => $candi,
message => 'Failed to build, but continuing with --force-build',
})
!! die("Aborting due to build failure: {$candi.dist.?identity // $candi.uri} (use --force-build to override)");
}
else {
self.logger.emit({
level => INFO,
stage => BUILD,
phase => AFTER,
message => "Building [OK] for {$candi.dist.?identity // $candi.as}",
});
}
take $candi;
}
@built
}
# xxx: needs some love
method test(:@includes, *@candidates ($, *@)) {
my $dispatcher := $*PERL.compiler.version < v2018.08
?? @candidates
!! @candidates.hyper(:batch(1), :degree($!test-degree || 1));
my @tested = $dispatcher.map: -> $candi {
self.logger.emit({
level => INFO,
stage => TEST,
phase => BEFORE,
message => "Testing: {$candi.dist.?identity // $candi.as}",
});
my $result := $!tester.test($candi, :includes($candi.dist.metainfo // []), :$!logger, :timeout($!test-timeout)).cache;
$candi.test-results = $result;
if $result.grep(*.not).elems {
self.logger.emit({
level => ERROR,
stage => TEST,
phase => AFTER,
message => "Testing [FAIL]: {$candi.dist.?identity // $candi.as}",
});
$!force-test
?? $!logger.emit({
level => ERROR,
stage => TEST,
phase => LIVE,
candi => $candi,
message => 'Failed to get passing tests, but continuing with --force-test',
})
!! die("Aborting due to test failure: {$candi.dist.?identity // $candi.uri} (use --force-test to override)");
}
else {
self.logger.emit({
level => INFO,
stage => TEST,
phase => AFTER,
message => "Testing [OK] for {$candi.dist.?identity // $candi.as}",
});
}
$candi;
}
return @tested
}
# xxx: needs some love
method search(*@identities ($, *@), *%fields, Bool :$strict = False) {
$!recommendation-manager.search(@identities, :$strict, |%fields);
}
method uninstall(CompUnit::Repository :@from!, *@identities) {
my @specs = @identities.map: { Zef::Distribution::DependencySpecification.new($_) }
eager gather for self.list-installed(@from) -> $candi {
my $dist = $candi.dist;
if @specs.first({ $dist.spec-matcher($_) }) {
my $cur = CompUnit::RepositoryRegistry.repository-for-spec("inst#{$candi.from}", :next-repo($*REPO));
$cur.uninstall($dist.compat);
take $candi;
}
}
}
method install(:@curs, *@candidates ($, *@)) {
my @installed = eager gather for @candidates -> $candi {
self.logger.emit({
level => INFO,
stage => INSTALL,
phase => BEFORE,
message => "Installing: {$candi.dist.?identity // $candi.as}",
});
for @curs -> $cur {
KEEP self.logger.emit({
level => VERBOSE,
stage => INSTALL,
phase => AFTER,
message => "Install [OK] for {$candi.dist.?identity // $candi.as}",
});
CATCH {
when /'already installed'/ {
self.logger.emit({
level => INFO,
stage => INSTALL,
phase => AFTER,
message => "Install [SKIP] for {$candi.dist.?identity // $candi.as}: {$_}",
});
}
default {
self.logger.emit({
level => ERROR,
stage => INSTALL,
phase => AFTER,
message => "Install [FAIL] for {$candi.dist.?identity // $candi.as}: {$_}",
});
$_.rethrow;
}
}
# Previously we put this through the deprecation CURI.install shim no matter what,
# but that doesn't play nicely with relative paths. We want to keep the original meta
# paths for newer rakudos so we must avoid using :absolute for the source paths by
# using the newer CURI.install if available
take $candi if $!installer.install($candi, :$cur, :force($!force-install), :timeout($!install-timeout));
}
}
return @installed;
}
# Unlike test/build/install/etc methods, this organizes multiples phases for multiples candidates.
# Eventually this will move back to a role/task based method of managing such phase dependencies.
method make-install(
CompUnit::Repository :@to!, # target CompUnit::Repository
Bool :$fetch = True, # try fetching whats missing
Bool :$build = True, # run Build.pm (DEPRECATED..?)
Bool :$test = True, # run tests
Bool :$dry, # do everything *but* actually install
Bool :$upgrade, # NYI
Bool :$serial,
*@candidates ($, *@),
*%_
) {
my @curs = @to.grep: -> $cur {
UNDO {
self.logger.emit({
level => WARN,
stage => INSTALL,
phase => BEFORE,
message => "CompUnit::Repository install target is not writeable/installable: {$cur}"
});
}
KEEP {
self.logger.emit({
level => TRACE,
stage => INSTALL,
phase => BEFORE,
message => "CompUnit::Repository install target is valid: {$cur}"
});
}
$cur.?can-install || next();
}
die "Need a valid installation target to continue" unless ?$dry || +@curs;
# XXX: Each loop block below essentially represents a phase, so they will probably
# be moved into their own method/module related directly to their phase. For now
# lumping them here allows us to easily move functionality between phases until we
# find the perfect balance/structure.
die "Must specify something to install" unless +@candidates;
# Fetch Stage:
# Use the results from searching Repositorys and download/fetch the distributions they point at
my @fetched-candidates = eager gather for @candidates -> $store {
# Note that this method of not fetching Zef::Distribution::Local means we cannot
# show fetching messages that would be fired in self.fetch(|) ( such as the download uri ).
# The reason it doesn't just fetch regardless is because it avoids caching local dev dists
# ala `zef install .` from polluting the name/auth/api/ver namespace of the local cache.
# TODO: Find a solution for the issues noted above which will resolve GH#261 "zef install should tell user where the install was from"
take $_ for ($store.dist.^name.contains('Zef::Distribution::Local') || !$fetch) ?? $store !! self.fetch($store, |%_);
}
die "Failed to fetch any candidates. No reason to proceed" unless +@fetched-candidates;
# Filter Stage:
# Handle stuff like removing distributions that are already installed, that don't have
# an allowable license, etc. It faces the same "fetch an alternative if available on failure"
# problem outlined below under `Sort Phase` (a depends on [A, B] where A gets filtered out
# below because it has the wrong license means we don't need anything that depends on A but
# *do* need to replace those items with things depended on by B [which replaces A])
my @filtered-candidates = @fetched-candidates.grep: -> $candi {
my $*error;
self.logger.emit({
level => DEBUG,
stage => FILTER,
phase => BEFORE,
message => "Filtering: {$candi.dist.identity}",
});
KEEP $!logger.emit({
level => DEBUG,
stage => FILTER,
phase => AFTER,
message => "Filtering [OK] for {$candi.dist.?identity // $candi.as}",
});
UNDO $!logger.emit({
level => ERROR,
stage => FILTER,
phase => AFTER,
message => "Filtering [FAIL] for {$candi.dist.?identity // $candi.as}: {$*error}",
});
$*error = do given $!config {
when ..?chars && any(|.