├── .gitignore ├── .gitpan ├── .travis.yml ├── Build.PL ├── MANIFEST ├── MANIFEST.SKIP ├── META.json ├── META.yml ├── README.md ├── bin ├── delete_all_repos ├── gitpan-import └── make_dist_local ├── inc └── MyBuilder.pm ├── lib ├── Gitpan.pm └── Gitpan │ ├── CPAN │ ├── Author.pm │ └── Authors.pm │ ├── Config.pm │ ├── ConfigFile.pm │ ├── Dist.pm │ ├── Git.pm │ ├── Github.pm │ ├── OO.pm │ ├── Release.pm │ ├── Repo.pm │ ├── Role │ ├── CanBackoff.pm │ ├── CanDistLog.pm │ ├── CanLog.pm │ ├── HasBackpanIndex.pm │ ├── HasCPANAuthors.pm │ ├── HasConfig.pm │ └── HasUA.pm │ ├── Types.pm │ └── perl5i.pm ├── src └── BackPAN-Index-0.42_02.tar.gz └── t ├── CanBackoff.t ├── CanDistLog.t ├── CanLog.t ├── Config.t ├── ConfigFile.t ├── Dist.t ├── Dist └── import_releases.t ├── Git.t ├── Git └── prepare_for_commits.t ├── Github.t ├── Gitpan.t ├── HasBackpanIndex.t ├── HasCPANAuthors.t ├── HasConfig.t ├── HasUA.t ├── OO.t ├── Release.t ├── Release └── extract.t ├── Repo.t ├── Repo ├── prepare_for_commits.t ├── prepare_for_push.t └── sync_with_github.t ├── Test.t ├── Types.t ├── backpan ├── authors │ ├── 01mailrc.txt.gz │ ├── 02authors.txt.gz │ └── id │ │ ├── A │ │ ├── AA │ │ │ └── AANOAA │ │ │ │ ├── Acme-eng2kor-0.0.1.tar.gz │ │ │ │ └── Acme-eng2kor-v0.0.2.tar.gz │ │ └── AQ │ │ │ └── AQUILINA │ │ │ ├── Acme-Warn-LOLCAT-0.01.tar.gz │ │ │ └── Acme-Warn-LOLCAT-0.02.tar.gz │ │ ├── C │ │ ├── CH │ │ │ └── CHRWIN │ │ │ │ └── date-spoken-german │ │ │ │ ├── Date-Spoken-German-0.04.tar.gz │ │ │ │ ├── Date-Spoken-German-0.05.tar.gz │ │ │ │ ├── date-spoken-german-0.02.tar.gz │ │ │ │ └── date-spoken-german-0.03.tar.gz │ │ └── CN │ │ │ └── CNANDOR │ │ │ └── Bundle-Slash-2.11.tar.gz │ │ ├── D │ │ ├── DC │ │ │ └── DCANTRELL │ │ │ │ ├── Acme-Pony-1.1.1.tar.gz │ │ │ │ └── Acme-Pony-1.1.2.tar.gz │ │ └── DH │ │ │ └── DHOSS │ │ │ ├── Acme-Blarghy-McBlarghBlargh-0.001-03-09-2009.tar.gz │ │ │ ├── Acme-Blarghy-McBlarghBlargh-0.002.tar.gz │ │ │ ├── Acme-Blarghy-McBlarghBlargh-0.0021.tar.gz │ │ │ ├── Acme-Blarghy-McBlarghBlargh-0.0022.tar.gz │ │ │ └── Acme-Blarghy-McBlarghBlargh.tar.gz │ │ ├── E │ │ └── ET │ │ │ └── ETHER │ │ │ ├── Acme-LookOfDisapproval-0.001.tar.gz │ │ │ ├── Acme-LookOfDisapproval-0.002.tar.gz │ │ │ ├── Acme-LookOfDisapproval-0.003.tar.gz │ │ │ ├── Acme-LookOfDisapproval-0.004.tar.gz │ │ │ ├── Acme-LookOfDisapproval-0.005.tar.gz │ │ │ └── Acme-LookOfDisapproval-0.006.tar.gz │ │ ├── F │ │ └── FL │ │ │ └── FLORA │ │ │ ├── Math-BigInt-1.90.tar.gz │ │ │ ├── Math-BigInt-1.91.tar.gz │ │ │ ├── Math-BigInt-1.92.tar.gz │ │ │ ├── Math-BigInt-1.93.tar.gz │ │ │ ├── Math-BigInt-1.94-TRIAL.tar.gz │ │ │ ├── Math-BigInt-1.95.tar.gz │ │ │ ├── Math-BigInt-1.96.tar.gz │ │ │ ├── Math-BigInt-1.97.tar.gz │ │ │ ├── Math-BigInt-1.98.tar.gz │ │ │ └── Math-BigInt-1.99.tar.gz │ │ ├── H │ │ └── HK │ │ │ └── HKOCH │ │ │ ├── reform-0.1.tar.gz │ │ │ ├── reform-0.2.tar.gz │ │ │ └── reform-0.3.tar.gz │ │ ├── J │ │ ├── JE │ │ │ └── JESSE │ │ │ │ └── Acme-Buffy-1.3.tar.gz │ │ └── JP │ │ │ └── JPEACOCK │ │ │ └── Math-BigInt-0.01.tar.gz │ │ ├── K │ │ └── KE │ │ │ └── KENFOX │ │ │ ├── perl-c++-api.0.0_2.tar.gz │ │ │ └── perl-c++-api.0.0_3.tar.gz │ │ ├── L │ │ ├── LB │ │ │ └── LBROCARD │ │ │ │ ├── Acme-Buffy-1.1.tar.gz │ │ │ │ ├── Acme-Buffy-1.2.tar.gz │ │ │ │ ├── Acme-Buffy-1.3.tar.gz │ │ │ │ ├── Acme-Buffy-1.4.tar.gz │ │ │ │ ├── Acme-Buffy-1.5.tar.gz │ │ │ │ └── Acme-Buffy-1.6.tar.gz │ │ └── LD │ │ │ └── LDACHARY │ │ │ ├── URIC-2.02.tar.gz │ │ │ ├── uri-2.00.tar.gz │ │ │ └── uri-2.01.tar.gz │ │ ├── M │ │ └── MA │ │ │ ├── MACGYVER │ │ │ ├── SMTP-Server-1.0.tar.gz │ │ │ └── SMTP-Server-1.1.tar.gz │ │ │ ├── MARCEL │ │ │ └── -0.01.tar.gz │ │ │ └── MAXA │ │ │ ├── Acme-Maybe-0.01.tar.gz │ │ │ └── Acme-Maybe-0.02.tar.gz │ │ ├── P │ │ ├── PA │ │ │ └── PAWAPAWA │ │ │ │ └── Lingua-JA-WordNet-0.21.tar.gz │ │ └── PJ │ │ │ └── PJACKLAM │ │ │ ├── Math-BigInt-1.99.1.tar.gz │ │ │ ├── Math-BigInt-1.9902.tar.gz │ │ │ ├── Math-BigInt-1.991.tar.gz │ │ │ ├── Math-BigInt-1.992.tar.gz │ │ │ ├── Math-BigInt-1.993.tar.gz │ │ │ ├── Math-BigInt-1.997.tar.gz │ │ │ ├── Math-BigInt-1.9993.tar.gz │ │ │ └── Math-BigInt-1.99_05.tar.gz │ │ ├── R │ │ └── RF │ │ │ └── RFREIMUTH │ │ │ └── RandomJungle-0.05.tar.gz │ │ ├── S │ │ └── SH │ │ │ └── SHARYANTO │ │ │ └── ReForm-0.00.tar.gz │ │ └── T │ │ └── TE │ │ └── TELS │ │ ├── Math-BigInt-1.35.tar.gz │ │ ├── Math-BigInt-1.36.tar.gz │ │ ├── Math-BigInt-1.37.tar.gz │ │ ├── Math-BigInt-1.38.tar.gz │ │ ├── Math-BigInt-1.39.tar.gz │ │ ├── Math-BigInt-1.40.tar.gz │ │ ├── Math-BigInt-1.41.tar.gz │ │ ├── Math-BigInt-1.42.tar.gz │ │ ├── Math-BigInt-1.43.tar.gz │ │ ├── Math-BigInt-1.44.tar.gz │ │ ├── Math-BigInt-1.45.tar.gz │ │ ├── Math-BigInt-1.46.tar.gz │ │ ├── Math-BigInt-1.47.tar.gz │ │ ├── Math-BigInt-1.48.tar.gz │ │ ├── Math-BigInt-1.49.tar.gz │ │ ├── Math-BigInt-1.53.tar.gz │ │ ├── Math-BigInt-1.54.tar.gz │ │ ├── Math-BigInt-1.55.tar.gz │ │ ├── Math-BigInt-1.56.tar.gz │ │ └── math │ │ ├── Math-BigInt-1.57.tar.gz │ │ ├── Math-BigInt-1.58.tar.gz │ │ ├── Math-BigInt-1.59.tar.gz │ │ ├── Math-BigInt-1.60.tar.gz │ │ ├── Math-BigInt-1.61.tar.gz │ │ ├── Math-BigInt-1.62.tar.gz │ │ ├── Math-BigInt-1.63.tar.gz │ │ ├── Math-BigInt-1.64.tar.gz │ │ ├── Math-BigInt-1.65.tar.gz │ │ ├── Math-BigInt-1.66.tar.gz │ │ ├── Math-BigInt-1.67.tar.gz │ │ ├── Math-BigInt-1.68.tar.gz │ │ ├── Math-BigInt-1.69.tar.gz │ │ ├── Math-BigInt-1.70.tar.gz │ │ ├── Math-BigInt-1.71.tar.gz │ │ ├── Math-BigInt-1.72.tar.gz │ │ ├── Math-BigInt-1.73.tar.gz │ │ ├── Math-BigInt-1.74.tar.gz │ │ ├── Math-BigInt-1.75.tar.gz │ │ ├── Math-BigInt-1.76.tar.gz │ │ ├── Math-BigInt-1.77.tar.gz │ │ ├── Math-BigInt-1.78.tar.gz │ │ ├── Math-BigInt-1.79.tar.gz │ │ ├── Math-BigInt-1.80.tar.gz │ │ ├── Math-BigInt-1.81.tar.gz │ │ ├── Math-BigInt-1.82.tar.gz │ │ ├── Math-BigInt-1.83.tar.gz │ │ ├── Math-BigInt-1.84.tar.gz │ │ ├── Math-BigInt-1.85.tar.gz │ │ ├── Math-BigInt-1.86.tar.gz │ │ ├── Math-BigInt-1.87.tar.gz │ │ ├── Math-BigInt-1.88.tar.gz │ │ └── Math-BigInt-1.89.tar.gz ├── backpan-index.gz └── modules │ └── 02packages.details.txt.gz ├── import ├── Math-BigInt.t ├── bad_links.t ├── multiple_releases_same_version.t ├── normalization.t ├── perl-c\+\+-api.t ├── simple_single_dist.t ├── update.t ├── version_normalization.t └── with_git_dir.t ├── lib └── Gitpan │ └── Test.pm └── perl5i.t /.gitignore: -------------------------------------------------------------------------------- 1 | gitpan-build.dmg 2 | *.bak 3 | *~ 4 | *.old 5 | Makefile 6 | blib/ 7 | pm_to_blib 8 | Build 9 | _build 10 | cover_db/ 11 | nytprof* 12 | *.patch 13 | .DS_Store 14 | MYMETA.* 15 | *.o 16 | gitpan-build 17 | t/gitpan 18 | t/caches/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 3 | - "5.18" 4 | env: 5 | - secure: "EOModDmXL8VA4AbV1BeNeQFqp8PHDFBzVB2gjyzKWCudqX7Byv6BNNSk28q7OVzk2H6Su22upzyWtdluhGIAdj9cwxbJJ9uVQ5512yPtdjdiznDK+55kcKVKdD1RVewieXKZeHyIWO9Ffn2KhDN0NzGo/3u9kvqDB/LxdyOxqbA=" 6 | -------------------------------------------------------------------------------- /Build.PL: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib 'inc'; 7 | 8 | use MyBuilder; 9 | 10 | my $build = MyBuilder->new( 11 | dist_name => 'Gitpan', 12 | module_name => "Gitpan", 13 | dist_author => 'Michael G. Schwern ', 14 | dist_abstract => "Put each CPAN distribution into its own Github repo", 15 | 16 | configure_requires => { 17 | "Module::Build" => 0.42, # For test_requires 18 | "Path::Tiny" => 0.054, 19 | }, 20 | 21 | requires => { 22 | perl => '5.18.1', 23 | 24 | "Archive::Extract" => '0.72', 25 | "Archive::Zip" => '1.39', 26 | "BackPAN::Index" => 0.39, 27 | "Email::Valid" => '1.195', 28 | "Git::Raw" => '0.49', 29 | "Git::Repository" => 1.300, 30 | "Git::Repository::Log" => 1.312, 31 | "Git::Repository::Status" => 0.03, # ->ignored fix 32 | "Import::Into" => '1.002000', 33 | "Method::Signatures" => '20141021', 34 | "Moo" => '1.006000', # DIE handler fix 35 | "Parallel::ForkManager" => '1.06', 36 | "Path::Tiny" => 0.059, # append_utf8 don't blow over $@ 37 | "perl5i::2" => "v2.13.0", 38 | "Pithub" => '0.01027', # caching, attr override, branch info 39 | "Type::Library" => 0.046, 40 | "Type::Utils" => 0.046, 41 | "Types::Standard" => 0.046, 42 | "YAML::XS" => 0, 43 | }, 44 | 45 | test_requires => { 46 | "Test::Most" => 0, 47 | "Test::TypeConstraints" => 0.05, 48 | }, 49 | 50 | # Avoid picking up tests in t/gitpan/repo/SO/Some-Dist/t/* 51 | test_files => "t/*.t t/*/*.t t/*/*/*.t", 52 | ); 53 | $build->create_build_script; 54 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | .gitpan 2 | bin/delete_all_repos 3 | bin/gitpan-import 4 | bin/make_dist_local 5 | Build.PL 6 | inc/MyBuilder.pm 7 | lib/Gitpan.pm 8 | lib/Gitpan/Config.pm 9 | lib/Gitpan/ConfigFile.pm 10 | lib/Gitpan/CPAN/Author.pm 11 | lib/Gitpan/CPAN/Authors.pm 12 | lib/Gitpan/Dist.pm 13 | lib/Gitpan/Git.pm 14 | lib/Gitpan/Github.pm 15 | lib/Gitpan/OO.pm 16 | lib/Gitpan/perl5i.pm 17 | lib/Gitpan/Release.pm 18 | lib/Gitpan/Repo.pm 19 | lib/Gitpan/Role/CanBackoff.pm 20 | lib/Gitpan/Role/CanDistLog.pm 21 | lib/Gitpan/Role/CanLog.pm 22 | lib/Gitpan/Role/HasBackpanIndex.pm 23 | lib/Gitpan/Role/HasConfig.pm 24 | lib/Gitpan/Role/HasCPANAuthors.pm 25 | lib/Gitpan/Role/HasUA.pm 26 | lib/Gitpan/Types.pm 27 | MANIFEST This list of files 28 | MANIFEST.SKIP 29 | META.json 30 | META.yml 31 | README.md 32 | src/BackPAN-Index-0.42_02.tar.gz 33 | t/backpan/authors/01mailrc.txt.gz 34 | t/backpan/authors/02authors.txt.gz 35 | t/backpan/authors/id/A/AA/AANOAA/Acme-eng2kor-0.0.1.tar.gz 36 | t/backpan/authors/id/A/AA/AANOAA/Acme-eng2kor-v0.0.2.tar.gz 37 | t/backpan/authors/id/A/AQ/AQUILINA/Acme-Warn-LOLCAT-0.01.tar.gz 38 | t/backpan/authors/id/A/AQ/AQUILINA/Acme-Warn-LOLCAT-0.02.tar.gz 39 | t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/date-spoken-german-0.02.tar.gz 40 | t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/date-spoken-german-0.03.tar.gz 41 | t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.04.tar.gz 42 | t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.05.tar.gz 43 | t/backpan/authors/id/C/CN/CNANDOR/Bundle-Slash-2.11.tar.gz 44 | t/backpan/authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.1.tar.gz 45 | t/backpan/authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.2.tar.gz 46 | t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.001-03-09-2009.tar.gz 47 | t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.002.tar.gz 48 | t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.0021.tar.gz 49 | t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.0022.tar.gz 50 | t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh.tar.gz 51 | t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.001.tar.gz 52 | t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.002.tar.gz 53 | t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.003.tar.gz 54 | t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.004.tar.gz 55 | t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.005.tar.gz 56 | t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.006.tar.gz 57 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.90.tar.gz 58 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.91.tar.gz 59 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.92.tar.gz 60 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.93.tar.gz 61 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.94-TRIAL.tar.gz 62 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.95.tar.gz 63 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.96.tar.gz 64 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.97.tar.gz 65 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.98.tar.gz 66 | t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.99.tar.gz 67 | t/backpan/authors/id/H/HK/HKOCH/reform-0.1.tar.gz 68 | t/backpan/authors/id/H/HK/HKOCH/reform-0.2.tar.gz 69 | t/backpan/authors/id/H/HK/HKOCH/reform-0.3.tar.gz 70 | t/backpan/authors/id/J/JE/JESSE/Acme-Buffy-1.3.tar.gz 71 | t/backpan/authors/id/J/JP/JPEACOCK/Math-BigInt-0.01.tar.gz 72 | t/backpan/authors/id/K/KE/KENFOX/perl-c++-api.0.0_2.tar.gz 73 | t/backpan/authors/id/K/KE/KENFOX/perl-c++-api.0.0_3.tar.gz 74 | t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.1.tar.gz 75 | t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.2.tar.gz 76 | t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.3.tar.gz 77 | t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.4.tar.gz 78 | t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.5.tar.gz 79 | t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.6.tar.gz 80 | t/backpan/authors/id/L/LD/LDACHARY/uri-2.00.tar.gz 81 | t/backpan/authors/id/L/LD/LDACHARY/uri-2.01.tar.gz 82 | t/backpan/authors/id/L/LD/LDACHARY/URIC-2.02.tar.gz 83 | t/backpan/authors/id/M/MA/MACGYVER/SMTP-Server-1.0.tar.gz 84 | t/backpan/authors/id/M/MA/MACGYVER/SMTP-Server-1.1.tar.gz 85 | t/backpan/authors/id/M/MA/MARCEL/-0.01.tar.gz 86 | t/backpan/authors/id/M/MA/MAXA/Acme-Maybe-0.01.tar.gz 87 | t/backpan/authors/id/M/MA/MAXA/Acme-Maybe-0.02.tar.gz 88 | t/backpan/authors/id/P/PA/PAWAPAWA/Lingua-JA-WordNet-0.21.tar.gz 89 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.99.1.tar.gz 90 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.9902.tar.gz 91 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.991.tar.gz 92 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.992.tar.gz 93 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.993.tar.gz 94 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.997.tar.gz 95 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.9993.tar.gz 96 | t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.99_05.tar.gz 97 | t/backpan/authors/id/S/SH/SHARYANTO/ReForm-0.00.tar.gz 98 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.35.tar.gz 99 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.36.tar.gz 100 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.37.tar.gz 101 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.38.tar.gz 102 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.39.tar.gz 103 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.40.tar.gz 104 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.41.tar.gz 105 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.42.tar.gz 106 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.43.tar.gz 107 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.44.tar.gz 108 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.45.tar.gz 109 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.46.tar.gz 110 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.47.tar.gz 111 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.48.tar.gz 112 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.49.tar.gz 113 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.53.tar.gz 114 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.54.tar.gz 115 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.55.tar.gz 116 | t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.56.tar.gz 117 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.57.tar.gz 118 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.58.tar.gz 119 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.59.tar.gz 120 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.60.tar.gz 121 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.61.tar.gz 122 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.62.tar.gz 123 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.63.tar.gz 124 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.64.tar.gz 125 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.65.tar.gz 126 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.66.tar.gz 127 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.67.tar.gz 128 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.68.tar.gz 129 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.69.tar.gz 130 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.70.tar.gz 131 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.71.tar.gz 132 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.72.tar.gz 133 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.73.tar.gz 134 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.74.tar.gz 135 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.75.tar.gz 136 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.76.tar.gz 137 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.77.tar.gz 138 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.78.tar.gz 139 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.79.tar.gz 140 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.80.tar.gz 141 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.81.tar.gz 142 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.82.tar.gz 143 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.83.tar.gz 144 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.84.tar.gz 145 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.85.tar.gz 146 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.86.tar.gz 147 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.87.tar.gz 148 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.88.tar.gz 149 | t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.89.tar.gz 150 | t/backpan/backpan-index.gz 151 | t/backpan/modules/02packages.details.txt.gz 152 | t/CanBackoff.t 153 | t/CanDistLog.t 154 | t/CanLog.t 155 | t/Config.t 156 | t/ConfigFile.t 157 | t/Dist.t 158 | t/Dist/import_releases.t 159 | t/Git.t 160 | t/Git/prepare_for_commits.t 161 | t/Github.t 162 | t/Gitpan.t 163 | t/HasBackpanIndex.t 164 | t/HasConfig.t 165 | t/HasCPANAuthors.t 166 | t/HasUA.t 167 | t/import/bad_links.t 168 | t/import/Math-BigInt.t 169 | t/import/multiple_releases_same_version.t 170 | t/import/normalization.t 171 | t/import/perl-c\+\+-api.t 172 | t/import/simple_single_dist.t 173 | t/import/version_normalization.t 174 | t/import/with_git_dir.t 175 | t/lib/Gitpan/Test.pm 176 | t/OO.t 177 | t/perl5i.t 178 | t/Release.t 179 | t/Release/extract.t 180 | t/Repo.t 181 | t/Repo/prepare_for_commits.t 182 | t/Repo/prepare_for_push.t 183 | t/Repo/sync_with_github.t 184 | t/Test.t 185 | t/Types.t 186 | -------------------------------------------------------------------------------- /MANIFEST.SKIP: -------------------------------------------------------------------------------- 1 | 2 | #!start included /Users/schwern/perl5/perlbrew/perls/perl-5.16.2-threads/lib/5.16.2/ExtUtils/MANIFEST.SKIP 3 | # Avoid version control files. 4 | \bRCS\b 5 | \bCVS\b 6 | \bSCCS\b 7 | ,v$ 8 | \B\.svn\b 9 | \B\.git\b 10 | \B\.gitignore\b 11 | \b_darcs\b 12 | \B\.cvsignore$ 13 | 14 | # Avoid VMS specific MakeMaker generated files 15 | \bDescrip.MMS$ 16 | \bDESCRIP.MMS$ 17 | \bdescrip.mms$ 18 | 19 | # Avoid Makemaker generated and utility files. 20 | \bMANIFEST\.bak 21 | \bMakefile$ 22 | \bblib/ 23 | \bMakeMaker-\d 24 | \bpm_to_blib\.ts$ 25 | \bpm_to_blib$ 26 | \bblibdirs\.ts$ # 6.18 through 6.25 generated this 27 | 28 | # Avoid Module::Build generated and utility files. 29 | \bBuild$ 30 | \b_build/ 31 | \bBuild.bat$ 32 | \bBuild.COM$ 33 | \bBUILD.COM$ 34 | \bbuild.com$ 35 | 36 | # Avoid temp and backup files. 37 | ~$ 38 | \.old$ 39 | \#$ 40 | \b\.# 41 | \.bak$ 42 | \.tmp$ 43 | \.# 44 | \.rej$ 45 | 46 | # Avoid OS-specific files/dirs 47 | # Mac OSX metadata 48 | \B\.DS_Store 49 | # Mac OSX SMB mount metadata files 50 | \B\._ 51 | 52 | # Avoid Devel::Cover and Devel::CoverX::Covered files. 53 | \bcover_db\b 54 | \bcovered\b 55 | 56 | # Avoid MYMETA files 57 | ^MYMETA\. 58 | #!end included /Users/schwern/perl5/perlbrew/perls/perl-5.16.2-threads/lib/5.16.2/ExtUtils/MANIFEST.SKIP 59 | 60 | submodules/ 61 | ^\.travis\.yml$ 62 | ^t/gitpan/ 63 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "Put each CPAN distribution into its own Github repo", 3 | "author" : [ 4 | "Michael G. Schwern " 5 | ], 6 | "dynamic_config" : 1, 7 | "generated_by" : "Module::Build version 0.421", 8 | "license" : [ 9 | "unknown" 10 | ], 11 | "meta-spec" : { 12 | "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", 13 | "version" : "2" 14 | }, 15 | "name" : "Gitpan", 16 | "prereqs" : { 17 | "configure" : { 18 | "requires" : { 19 | "Module::Build" : "0.42", 20 | "Path::Tiny" : "0.054" 21 | } 22 | }, 23 | "runtime" : { 24 | "requires" : { 25 | "Archive::Extract" : "0.72", 26 | "Archive::Zip" : "1.39", 27 | "BackPAN::Index" : "0.39", 28 | "Email::Valid" : "1.195", 29 | "Git::Raw" : "0.49", 30 | "Git::Repository" : "1.3", 31 | "Git::Repository::Log" : "1.312", 32 | "Git::Repository::Status" : "0.03", 33 | "Import::Into" : "1.002000", 34 | "Method::Signatures" : "20141021", 35 | "Moo" : "1.006000", 36 | "Parallel::ForkManager" : "1.06", 37 | "Path::Tiny" : "0.059", 38 | "Pithub" : "0.01027", 39 | "Type::Library" : "0.046", 40 | "Type::Utils" : "0.046", 41 | "Types::Standard" : "0.046", 42 | "YAML::XS" : "0", 43 | "perl" : "v5.18.1", 44 | "perl5i::2" : "v2.13.0" 45 | } 46 | }, 47 | "test" : { 48 | "requires" : { 49 | "Test::Most" : "0", 50 | "Test::TypeConstraints" : "0.05" 51 | } 52 | } 53 | }, 54 | "provides" : { 55 | "Gitpan" : { 56 | "file" : "lib/Gitpan.pm", 57 | "version" : "v2.3.0" 58 | }, 59 | "Gitpan::CPAN::Author" : { 60 | "file" : "lib/Gitpan/CPAN/Author.pm" 61 | }, 62 | "Gitpan::CPAN::Authors" : { 63 | "file" : "lib/Gitpan/CPAN/Authors.pm" 64 | }, 65 | "Gitpan::Config" : { 66 | "file" : "lib/Gitpan/Config.pm" 67 | }, 68 | "Gitpan::ConfigFile" : { 69 | "file" : "lib/Gitpan/ConfigFile.pm" 70 | }, 71 | "Gitpan::Dist" : { 72 | "file" : "lib/Gitpan/Dist.pm" 73 | }, 74 | "Gitpan::Dummy" : { 75 | "file" : "lib/Gitpan.pm" 76 | }, 77 | "Gitpan::Git" : { 78 | "file" : "lib/Gitpan/Git.pm" 79 | }, 80 | "Gitpan::Github" : { 81 | "file" : "lib/Gitpan/Github.pm" 82 | }, 83 | "Gitpan::OO" : { 84 | "file" : "lib/Gitpan/OO.pm" 85 | }, 86 | "Gitpan::Release" : { 87 | "file" : "lib/Gitpan/Release.pm" 88 | }, 89 | "Gitpan::Repo" : { 90 | "file" : "lib/Gitpan/Repo.pm" 91 | }, 92 | "Gitpan::Role::CanBackoff" : { 93 | "file" : "lib/Gitpan/Role/CanBackoff.pm" 94 | }, 95 | "Gitpan::Role::CanDistLog" : { 96 | "file" : "lib/Gitpan/Role/CanDistLog.pm" 97 | }, 98 | "Gitpan::Role::CanLog" : { 99 | "file" : "lib/Gitpan/Role/CanLog.pm" 100 | }, 101 | "Gitpan::Role::HasBackpanIndex" : { 102 | "file" : "lib/Gitpan/Role/HasBackpanIndex.pm" 103 | }, 104 | "Gitpan::Role::HasCPANAuthors" : { 105 | "file" : "lib/Gitpan/Role/HasCPANAuthors.pm" 106 | }, 107 | "Gitpan::Role::HasConfig" : { 108 | "file" : "lib/Gitpan/Role/HasConfig.pm" 109 | }, 110 | "Gitpan::Role::HasUA" : { 111 | "file" : "lib/Gitpan/Role/HasUA.pm" 112 | }, 113 | "Gitpan::Types" : { 114 | "file" : "lib/Gitpan/Types.pm" 115 | }, 116 | "Gitpan::perl5i" : { 117 | "file" : "lib/Gitpan/perl5i.pm" 118 | } 119 | }, 120 | "release_status" : "stable", 121 | "version" : "v2.3.0" 122 | } 123 | -------------------------------------------------------------------------------- /META.yml: -------------------------------------------------------------------------------- 1 | --- 2 | abstract: 'Put each CPAN distribution into its own Github repo' 3 | author: 4 | - 'Michael G. Schwern ' 5 | build_requires: 6 | Test::Most: '0' 7 | Test::TypeConstraints: '0.05' 8 | configure_requires: 9 | Module::Build: '0.42' 10 | Path::Tiny: '0.054' 11 | dynamic_config: 1 12 | generated_by: 'Module::Build version 0.421, CPAN::Meta::Converter version 2.142060' 13 | license: unknown 14 | meta-spec: 15 | url: http://module-build.sourceforge.net/META-spec-v1.4.html 16 | version: '1.4' 17 | name: Gitpan 18 | provides: 19 | Gitpan: 20 | file: lib/Gitpan.pm 21 | version: v2.3.0 22 | Gitpan::CPAN::Author: 23 | file: lib/Gitpan/CPAN/Author.pm 24 | Gitpan::CPAN::Authors: 25 | file: lib/Gitpan/CPAN/Authors.pm 26 | Gitpan::Config: 27 | file: lib/Gitpan/Config.pm 28 | Gitpan::ConfigFile: 29 | file: lib/Gitpan/ConfigFile.pm 30 | Gitpan::Dist: 31 | file: lib/Gitpan/Dist.pm 32 | Gitpan::Dummy: 33 | file: lib/Gitpan.pm 34 | Gitpan::Git: 35 | file: lib/Gitpan/Git.pm 36 | Gitpan::Github: 37 | file: lib/Gitpan/Github.pm 38 | Gitpan::OO: 39 | file: lib/Gitpan/OO.pm 40 | Gitpan::Release: 41 | file: lib/Gitpan/Release.pm 42 | Gitpan::Repo: 43 | file: lib/Gitpan/Repo.pm 44 | Gitpan::Role::CanBackoff: 45 | file: lib/Gitpan/Role/CanBackoff.pm 46 | Gitpan::Role::CanDistLog: 47 | file: lib/Gitpan/Role/CanDistLog.pm 48 | Gitpan::Role::CanLog: 49 | file: lib/Gitpan/Role/CanLog.pm 50 | Gitpan::Role::HasBackpanIndex: 51 | file: lib/Gitpan/Role/HasBackpanIndex.pm 52 | Gitpan::Role::HasCPANAuthors: 53 | file: lib/Gitpan/Role/HasCPANAuthors.pm 54 | Gitpan::Role::HasConfig: 55 | file: lib/Gitpan/Role/HasConfig.pm 56 | Gitpan::Role::HasUA: 57 | file: lib/Gitpan/Role/HasUA.pm 58 | Gitpan::Types: 59 | file: lib/Gitpan/Types.pm 60 | Gitpan::perl5i: 61 | file: lib/Gitpan/perl5i.pm 62 | requires: 63 | Archive::Extract: '0.72' 64 | Archive::Zip: '1.39' 65 | BackPAN::Index: '0.39' 66 | Email::Valid: '1.195' 67 | Git::Raw: '0.49' 68 | Git::Repository: '1.3' 69 | Git::Repository::Log: '1.312' 70 | Git::Repository::Status: '0.03' 71 | Import::Into: '1.002000' 72 | Method::Signatures: '20141021' 73 | Moo: '1.006000' 74 | Parallel::ForkManager: '1.06' 75 | Path::Tiny: '0.059' 76 | Pithub: '0.01027' 77 | Type::Library: '0.046' 78 | Type::Utils: '0.046' 79 | Types::Standard: '0.046' 80 | YAML::XS: '0' 81 | perl: v5.18.1 82 | perl5i::2: v2.13.0 83 | version: v2.3.0 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | What is Gitpan? 2 | --------------- 3 | 4 | Gitpan is a project to import the entire history of CPAN (known as 5 | BackPAN) into a set of git repositories, one per distribution. 6 | 7 | 8 | What is CPAN? 9 | ------------- 10 | 11 | CPAN is the Comprehensive Perl Archive Network at cpan.org. It is an 12 | archive of tens of thousands of Perl modules written by thousands of 13 | authors. A good interface to it is http://metacpan.org 14 | 15 | 16 | What is BackPAN? 17 | ---------------- 18 | 19 | In order to limit CPAN's size, authors are requested to delete old 20 | releases. BackPAN maintains all CPAN releases, even deleted ones, and 21 | is a complete history of CPAN. There are only a few BackPAN mirrors 22 | such as http://backpan.perl.org 23 | 24 | 25 | Why is Gitpan? 26 | -------------- 27 | 28 | CPAN (and thus BackPAN) is a pile of tarballs organized by author. It 29 | is difficult to get the complete history of a distribution, especially 30 | one that has changed authors or is released by multiple authors (for 31 | example, Moose). Because releases are regularly deleted from CPAN 32 | even sites like search.cpan.org provide an incomplete history. Having 33 | the complete history of each distribution in its own repository makes 34 | the full distribution history easy to access. 35 | 36 | Gitpan also hopes to make patching CPAN modules easier. Ideally you 37 | simply clone the Gitpan repository and work. New releases can be 38 | pulled and merged from Gitpan. 39 | 40 | Gitpan hopes to showcase using a repository as an archive format, 41 | rather than a pile of tarballs. A repository is far more useful than 42 | a pile of tarballs, and contrary to many people's expectations, the 43 | repository is turning out smaller. 44 | 45 | Finally, Gitpan is being created in the hope that "if you build it 46 | they will come". Getting data out of CPAN in an automated fashion has 47 | traditionally been difficult. 48 | 49 | 50 | Where is Gitpan? 51 | ---------------- 52 | 53 | The repositories are on github.com at http://github.com/gitpan. 54 | 55 | Code, discussion, and issues can be had at http://github.com/evalEmpire/gitpan. 56 | 57 | 58 | How do I access a distribution on Gitpan? 59 | ----------------------------------------- 60 | 61 | Simplest way is to go to http://github.com/gitpan/. 62 | For example, Acme-Pony can be found at http://github.com/gitpan/Acme-Pony. 63 | Instructions for futher access can be found there. 64 | 65 | The clone URL for a given distribution is git://github.com/gitpan/.git. 66 | You can clone without a github account. 67 | 68 | 69 | How big is BackPAN? 70 | ------------------- 71 | 72 | BackPAN (just the modules, we're not doing perl releases) contains 73 | over 230,000 archive files (mostly gzipped tarballs) representing over 74 | 35,000 distributions from over 6500 authors taking over 38 gigs of space. 75 | 76 | 77 | How big is Gitpan? 78 | ------------------ 79 | 80 | Gitpan consists of over 35,000 repositories representing each CPAN 81 | distribution. Disk usage (garbage collected repositories with the 82 | latest version checked out checkout) is about 30 gigs. It imported 83 | over 230,000 releases with a compressed size of over 33 gigs. 84 | 85 | 86 | Did Gitpan skip anything? 87 | ------------------------- 88 | 89 | Yes. Gitpan generally skips things which are not modules, such as 90 | scripts, binary releases, patches, documentation and other randomness 91 | which has been uploaded to CPAN over the years. Gitpan also skips 92 | releases of Perl and Parrot, they are large and there are plenty of 93 | better archives for that. 94 | 95 | Github has a limit of 100 megs per file, so Gitpan cannot archive the 96 | few files which go over that limit and will instead replace it with a 97 | dummy file indicating why it was skipped. 98 | 99 | Gitpan skips any archives which cannot be extracted. Sometimes they 100 | are corrupt beyond repair, sometimes they can be extracted with some 101 | manual effort. 102 | 103 | Occassionally something which is not a module will be considered of 104 | sufficient historical interest that it will be included, such as a 105 | heavily patched version of Perl for an obscure operating system, or a 106 | set of very old, well developed scripts. 107 | 108 | 109 | Will you be adding X to Gitpan? 110 | ------------------------------- 111 | 112 | The primary focus is to get accurate repositories for each CPAN 113 | distribution and to make this data available for others to use. When 114 | you think "will Gitpan do X" instead think "how can I use Gitpan to 115 | build X?" 116 | 117 | Suggestions on how to improve the data available from Gitpan heartily 118 | accepted. 119 | 120 | 121 | How can I merge Gitpan's history with my module? 122 | ------------------------------------------------ 123 | 124 | If you are the owner of a CPAN module and have an existing, but 125 | incomplete, repository you can fill in the history using Gitpan. The 126 | technique is outlined in this article. 127 | http://use.perl.org/use.perl.org/_schwern/journal/39974.html 128 | 129 | 130 | How do I update my module on Gitpan? 131 | ------------------------------------ 132 | 133 | Gitpan will automatically pull new releases from CPAN, you don't have 134 | to do anything. 135 | 136 | 137 | When does Gitpan update? 138 | - 139 | 140 | Updates happen about once a week. We hope to make them daily. 141 | 142 | 143 | Where can I get a list of all the repositories? 144 | ----------------------------------------------- 145 | 146 | You can get it from [Github's API](http://developer.github.com) by 147 | [listing all of Gitpan's 148 | repositories](https://developer.github.com/v3/repos/#list-organization-repositories). The list is rather large and will require multiple calls. See [Pagination](https://developer.github.com/v3/#pagination). 149 | 150 | 151 | How can I help? 152 | --------------- 153 | 154 | See http://github.com/evalEmpire/gitpan/issues for a list of open problems. 155 | 156 | You can also contribute by looking through imported CPAN distributions, 157 | checking for mistakes and reporting them as issues. 158 | 159 | 160 | I'm the author of X distribution and already have a repository, would you delete the Gitpan repo? 161 | ---------- 162 | 163 | Sorry, no. 164 | 165 | Gitpan is intended to co-exist with, not compete with, the development 166 | repository for a distribution. It provides a consistent, easy to find 167 | interface to your releases so you don't have to. 168 | 169 | Gitpan serves purposes different from the development repo. A Gitpan 170 | repo... 171 | 172 | * Has a consistent location. 173 | * Has a consistent structure. 174 | * Uses a consistent version control system. 175 | * Is only for releases. 176 | * Has the entire release history. 177 | 178 | While there are many different ways to use Gitpan, the primary use is 179 | to examine the release history of any given distribution regardless of 180 | the preferences of the distribution authors. 181 | 182 | Gitpan tries to make it as clear as possible that it is not a 183 | development repository (descriptive text, descriptive commits, no 184 | issue tracker, no wiki) and to point the user at the proper 185 | development resources (ie. the distribution's Metacpan page). 186 | 187 | You can make your own development repository more visible by adding a 188 | repository resource to your release meta-data. See 189 | https://metacpan.org/pod/CPAN::Meta::Spec#resources 190 | 191 | 192 | I'm the author of X distribution, can I get commit access to Gitpan? 193 | -------------------------------------------------------------------- 194 | 195 | Sorry, no. Gitpan is intentionally read-only to provide a consistent 196 | interface over all of CPAN. Allowing developers to commit directly to 197 | Gitpan would endanger this consistency. In this sense, Gitpan is 198 | simply a read-only view on your releases. 199 | 200 | As the developer of the project, you should continue to develop 201 | against your regular repo. However, it is helpful to fill in back 202 | history should you be missing it. You can use the release tags and 203 | dates on the Gitpan repo to place tags into your development repo. If 204 | history is completely missing, you can splice your development 205 | repository on top of the Gitpan repo. See "How can I merge Gitpan's 206 | history with my module?" above. 207 | 208 | You could develop off a Gitpan fork, but the actual development 209 | history of your project up to this point would be lost. Merging your 210 | dev repo with Gitpan is left as an exercise for the reader to do 211 | usefully. If you tag your releases in a consistent manner and publish 212 | the location of your repository, Gitpan doesn't offer anything new to 213 | the developer. 214 | 215 | 216 | I found a distribution that's out of date, could you update it? 217 | -- 218 | 219 | If it's less than a week old, wait another week and it will probably 220 | happen as part of the normal update process. 221 | 222 | If it's more than a week old, please report it to us at 223 | or 224 | schwern+gitpan@pobox.com. 225 | 226 | 227 | I noticed a problem with a repository 228 | ------------------------------------- 229 | 230 | Please report it at or to 231 | schwern+gitpan@pobox.com. 232 | 233 | 234 | Who do we have to thank for Gitpan? 235 | ----------------------------------- 236 | 237 | Gitpan exists on top of a pile of pre-existing technology and services. 238 | Very little new code was written and the yaks were already well shorn. 239 | 240 | * Elaine Ashton for instituting BackPAN. 241 | * Jarkko, Graham Barr and the rest of the CPAN cabal. 242 | * Andreas König for tirelessly maintaining PAUSE. 243 | * brian d foy for spearheading BackPAN archeology. 244 | * Léon Brocard for Parse::BACKPAN::Packages to access the backpan index and maintaining the BackPAN index. 245 | * Linus and the git devs for git (this was tried before on SVN and guh...) 246 | * github.com for a generous donation of space and support and angry unicorns 247 | * Integra Telecom for donating a server. 248 | * Yanick Champoux for git-cpan-patch which does most of the work. 249 | * ftp.funet.fi for an rsync-able BackPAN mirror. 250 | * Michael Schwern glued it all together. 251 | * And all the people who [contributed code](https://github.com/evalEmpire/gitpan/graphs/contributors) and [reported issues](https://github.com/evalEmpire/gitpan/issues?q=is%3Aissue+is%3Aclosed). 252 | 253 | 254 | How can I contact Gitpan? 255 | ------------------------- 256 | 257 | * Email: schwern+gitpan@pobox.com 258 | * Web: http://github.com/gitpan/ 259 | * Dev: http://github.com/evalEmpire/gitpan 260 | * Issues: http://github.com/evalEmpire/gitpan/issues 261 | * Twitter: #gitpan and http://twitter.com/gitpan 262 | -------------------------------------------------------------------------------- /bin/delete_all_repos: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Gitpan::Github; 6 | 7 | my $gh = Gitpan::Github->new( repo => "whatever" ); 8 | my $config = $gh->config; 9 | 10 | say "Are you sure you want to delete all repos owned by @{[ $config->github_owner ]}?"; 11 | my $ans = ; 12 | exit unless $ans =~ /^y/i; 13 | 14 | for( my $page = 1; 15 | my $data = $gh->query("GET", "/orgs/".$config->github_owner."/repos?page=$page&per_page=100&access_token=".$config->github_access_token); 16 | $page++ 17 | ) { 18 | delete_repos($data); 19 | } 20 | 21 | func delete_repos(ArrayRef $repos) { 22 | for my $repo (@$repos) { 23 | say "Deleting $repo->{name}"; 24 | die "$repo->{name} is owned by $repo->{owner}{login}" 25 | unless $repo->{owner}{login} eq $config->github_owner; 26 | $gh->repos->delete($config->github_owner, $repo->{name}); 27 | } 28 | 29 | return; 30 | } 31 | -------------------------------------------------------------------------------- /bin/gitpan-import: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Getopt::Long; 6 | use Gitpan; 7 | 8 | # Defaults 9 | my %opts = ( 10 | workers => 3, 11 | overwrite_repo => 0, 12 | ); 13 | 14 | # These are options which change what distributions are 15 | # imported. 16 | my @search_opts = qw( 17 | author 18 | random 19 | name_like 20 | since 21 | limit 22 | ); 23 | 24 | GetOptions( 25 | \%opts, 26 | "author=s", 27 | "random=i", 28 | "name_like=s", 29 | "since=s", 30 | "sort!", 31 | "limit=i", 32 | "workers=i", 33 | "overwrite_repo!", 34 | ); 35 | 36 | my $gitpan = Gitpan->new; 37 | 38 | if( @ARGV ) { 39 | $gitpan->import_from_distnames( 40 | \@ARGV, 41 | num_workers => $opts{workers}, 42 | overwrite_repo => $opts{overwrite_repo} 43 | ); 44 | } 45 | else { 46 | my $bp_dists = $gitpan->select_dists( 47 | limit => $opts{limit}, 48 | name_like => $opts{name_like}, 49 | author => $opts{author}, 50 | sort => $opts{sort}, 51 | random => $opts{random}, 52 | since => $opts{since}, 53 | ); 54 | $gitpan->import_from_backpan_dists( 55 | bp_dists => $bp_dists, 56 | all_dists => !_has_search_opts(\%opts), 57 | num_workers => $opts{workers}, 58 | overwrite_repo => $opts{overwrite_repo}, 59 | ); 60 | } 61 | 62 | 63 | func _has_search_opts( HashRef $opts ) { 64 | for my $search_opt (@search_opts) { 65 | return 1 if exists $opts->{$search_opt}; 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | __END__ 72 | 73 | =head1 NAME 74 | 75 | gitpan - Import all of BackPAN to github 76 | 77 | =head1 SYNOPSIS 78 | 79 | gitpan [options] [dist] [dist] ... 80 | 81 | =head1 DESCRIPTION 82 | 83 | gitpan is a script to import large chunks (or all of) BackPAN into git 84 | and github. It allows you to select BackPAN distributions to import 85 | and control importing to github. 86 | 87 | =head1 OPTIONS 88 | 89 | =head2 Distribution Selection 90 | 91 | By default, gitpan will import every distribution on BackPAN. You can 92 | override this by passing it a list of distributions on the command 93 | line or the following. 94 | 95 | =head3 --author 96 | 97 | Import all the options of a particular author. Give it their CPAN id. 98 | For example 99 | 100 | gitpan --author=MSCHWERN 101 | 102 | =head3 --random 103 | 104 | Pick N random distributions. 105 | 106 | gitpan --random=100 107 | 108 | =head3 --name_list 109 | 110 | Pick all distributions matching the given SQL LIKE pattern. 111 | 112 | # Import all the D's. 113 | gitpan --name_like='D%' 114 | 115 | =head3 --sort 116 | 117 | Sort the distributions by name before processing 118 | 119 | =head3 --since 120 | 121 | Pick only distributions which have changed since the given time. 122 | 123 | Time is given in Unix epoch time. 124 | 125 | gitpan --since=1200000000 126 | 127 | =head3 --limit 128 | 129 | Limit how many distributions are imported 130 | 131 | gitpan --limit=5 132 | 133 | 134 | =head2 Gitpan Details 135 | 136 | =head3 --workers 137 | 138 | Specify the number of worker processes to use. 139 | 140 | Default is 3. 141 | 142 | =head3 --overwrite_repo 143 | 144 | Delete the local and Github repo before importing. Use this if you 145 | want to reimport the whole distribution. 146 | 147 | Default is false. 148 | 149 | 150 | =head1 EXAMPLE 151 | 152 | Here is how gitpan does its importing. 153 | 154 | gitpan --name_like='F%' --sort 155 | 156 | Import all the distributions starting with "F" in name order. 157 | 158 | =cut 159 | -------------------------------------------------------------------------------- /bin/make_dist_local: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Given a distribution name, it will mirror all its releases into 4 | # our local BackPAN used for testing. 5 | # 6 | # example: perl bin/make_dist_local Math-BigInt 7 | 8 | use lib 'lib'; 9 | use Gitpan::perl5i; 10 | use Gitpan::Dist; 11 | use Gitpan::Release; 12 | 13 | my $dist = Gitpan::Dist->new( 14 | distname => shift 15 | ); 16 | 17 | my $backpan_dir = "t/backpan/"->path; 18 | 19 | for my $backpan_release ($dist->backpan_releases->all) { 20 | my $path = $backpan_release->path; 21 | say $path; 22 | 23 | my $release = $dist->release_from_backpan($backpan_release); 24 | $release->get; 25 | 26 | my $dest = $backpan_dir->child($path); 27 | $dest->touchpath; 28 | $release->archive_file->move($dest); 29 | } 30 | -------------------------------------------------------------------------------- /inc/MyBuilder.pm: -------------------------------------------------------------------------------- 1 | package MyBuilder; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use base 'Module::Build'; 7 | 8 | use v5.12; 9 | use Path::Tiny; 10 | 11 | sub ACTION_build { 12 | my $self = shift; 13 | 14 | # Install distributions from src/ as needed. 15 | for my $archive (path( $self->base_dir, "src" )->children) { 16 | my($dist, $version) = $archive->basename =~ /^(.*)-([\d_.]+)(?:-TRIAL)?\./; 17 | my $module = $dist; 18 | $module =~ s{-}{::}g; 19 | if( !eval "require $module" or 20 | version->parse($version) > version->parse($module->VERSION) 21 | ) { 22 | say "Installing $archive."; 23 | system "cpanm", "-n", $archive; 24 | } 25 | } 26 | 27 | return $self->SUPER::ACTION_build; 28 | } 29 | 30 | sub find_test_files { 31 | my $self = shift; 32 | 33 | my $tests = $self->SUPER::find_test_files; 34 | 35 | return [sort { lc $a cmp lc $b } @$tests]; 36 | } 37 | 38 | 1; 39 | -------------------------------------------------------------------------------- /lib/Gitpan.pm: -------------------------------------------------------------------------------- 1 | package Gitpan; 2 | 3 | use Gitpan::perl5i; 4 | use Gitpan::OO; 5 | use Gitpan::Types; 6 | 7 | use version; our $VERSION = qv("v2.3.0"); 8 | 9 | use Gitpan::Dist; 10 | use Parallel::ForkManager; 11 | 12 | with 'Gitpan::Role::HasBackpanIndex', 13 | 'Gitpan::Role::HasConfig', 14 | 'Gitpan::Role::CanLog'; 15 | 16 | { 17 | package Gitpan::Dummy; 18 | use Gitpan::OO; 19 | with 'Gitpan::Role::HasCPANAuthors', 20 | 'Gitpan::Role::HasBackpanIndex'; 21 | } 22 | 23 | 24 | method import_from_distnames( 25 | ArrayRef $distnames, 26 | Int :$num_workers, 27 | Bool :$overwrite_repo 28 | ) { 29 | my $idx = 0; 30 | my $iter = sub { 31 | return $distnames->[$idx++]; 32 | }; 33 | 34 | return $self->import_from_iterator( 35 | iterator => $iter, 36 | num_workers => $num_workers, 37 | overwrite_repo => $overwrite_repo 38 | ); 39 | } 40 | 41 | 42 | method select_dists( 43 | DBIx::Class::ResultSet :$bp_dists = $self->backpan_index->dists, 44 | Maybe[Int] :$limit, 45 | Maybe[Str] :$name_like, 46 | Maybe[Str] :$author, 47 | Bool :$sort, 48 | Maybe[Int] :$random, 49 | Maybe[Str] :$since 50 | ) { 51 | $bp_dists = $bp_dists->search({ latest_date => \">= $since" }) if $since; 52 | 53 | $bp_dists = $bp_dists->search( undef, { order_by => \'random()', 54 | rows => $random } ) if $random; 55 | 56 | $bp_dists = $bp_dists->search( undef, { order_by => 'name' } ) if $sort; 57 | 58 | $bp_dists = $bp_dists->search({ "releases.cpanid" => $author }, 59 | { join => "releases", distinct => 1 }) if $author; 60 | 61 | $bp_dists = $bp_dists->search({ name => { -like => $name_like } }) if $name_like; 62 | 63 | $bp_dists = $bp_dists->search( undef, { rows => $limit }) if $limit; 64 | 65 | return $bp_dists; 66 | } 67 | 68 | 69 | method import_from_backpan_dists( 70 | DBIx::Class::ResultSet :$bp_dists!, 71 | Bool :$all_dists, 72 | Int :$num_workers, 73 | Bool :$overwrite_repo, 74 | ) { 75 | if( $all_dists ) { 76 | my $since = $self->read_latest_release_timestamp; 77 | $bp_dists = $self->select_dists( 78 | bp_dists => $bp_dists, 79 | since => $since 80 | ); 81 | $self->main_log("Importing @{[$bp_dists->count]} dists changed since $since"); 82 | } 83 | 84 | my $iter = sub { 85 | my $bp_dist = $bp_dists->next; 86 | return unless $bp_dist; 87 | return $bp_dist->name; 88 | }; 89 | 90 | my $latest_release = $self->latest_release_date; 91 | 92 | my $ret = $self->import_from_iterator( 93 | iterator => $iter, 94 | num_workers => $num_workers, 95 | overwrite_repo => $overwrite_repo 96 | ); 97 | 98 | $self->write_latest_release_timestamp($latest_release) if $all_dists; 99 | 100 | return $ret; 101 | } 102 | 103 | 104 | method import_from_iterator( 105 | CodeRef :$iterator!, 106 | Int :$num_workers //= 2, 107 | Bool :$overwrite_repo = 0, 108 | ) { 109 | my $fork_man = Parallel::ForkManager->new($num_workers); 110 | 111 | my $config = $self->config; 112 | 113 | # Parse the CPAN author's file in the parent so each child doesn't 114 | # have to redo the work. 115 | Gitpan::Dummy->new->build_cpan_authors; 116 | 117 | # Make sure the BackPAN index database is created else every 118 | # child will try to make it at the same time 119 | Gitpan::Dummy->new->backpan_index; 120 | 121 | $self->main_log("Starting import at @{[gmtime->iso8601]}"); 122 | 123 | while( my $distname = $iterator->() ) { 124 | if( $config->skip_dist($distname) ) { 125 | $self->main_log( "Skipping $distname due to config" ); 126 | next; 127 | } 128 | 129 | my $pid = $fork_man->start and next; 130 | $self->import_from_distname( 131 | $distname, 132 | overwrite_repo => $overwrite_repo 133 | ); 134 | $fork_man->finish; 135 | } 136 | 137 | $fork_man->wait_all_children; 138 | 139 | $self->main_log("Import complete at @{[gmtime->iso8601]}"); 140 | 141 | return; 142 | } 143 | 144 | 145 | method import_from_distname( 146 | Str $name, 147 | Bool :$overwrite_repo = 0 148 | ) { 149 | $self->import_dist( 150 | Gitpan::Dist->new( distname => $name ), 151 | overwrite_repo => $overwrite_repo 152 | ); 153 | } 154 | 155 | 156 | method import_dist( 157 | Gitpan::Dist $dist, 158 | Bool :$overwrite_repo = 0, 159 | ) { 160 | $dist->delete_repo if $overwrite_repo; 161 | $dist->import_releases; 162 | } 163 | 164 | 165 | method latest_release_date() { 166 | return $self->backpan_index->releases->get_column("date")->max; 167 | } 168 | 169 | 170 | method read_latest_release_timestamp() { 171 | my $timestamp_file = $self->config->gitpan_latest_imported_release_timestamp; 172 | return 0 if !-e $timestamp_file; 173 | 174 | my($timestamp) = $timestamp_file->lines( 175 | {count => 1, chomp => 1} 176 | ); 177 | return $timestamp; 178 | } 179 | 180 | 181 | method write_latest_release_timestamp( Str $date ) { 182 | my $timestamp_file = $self->config->gitpan_latest_imported_release_timestamp; 183 | return $timestamp_file->spew($date); 184 | } 185 | -------------------------------------------------------------------------------- /lib/Gitpan/CPAN/Author.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::CPAN::Author; 2 | 3 | use Gitpan::perl5i; 4 | use Gitpan::OO; 5 | use Gitpan::Types; 6 | 7 | haz name => 8 | is => 'ro', 9 | isa => Str, 10 | required => 1; 11 | 12 | haz email => 13 | is => 'ro', 14 | isa => Str, 15 | default => ''; 16 | 17 | haz url => 18 | is => 'ro', 19 | isa => URI, 20 | default => ''; 21 | 22 | haz cpanid => 23 | is => 'ro', 24 | isa => Str, 25 | required => 1; 26 | -------------------------------------------------------------------------------- /lib/Gitpan/CPAN/Authors.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::CPAN::Authors; 2 | 3 | use Gitpan::perl5i; 4 | use Gitpan::OO; 5 | use Gitpan::Types; 6 | 7 | use Gitpan::CPAN::Author; 8 | use Encode; 9 | use Email::Valid; 10 | 11 | haz __authors => 12 | is => 'ro', 13 | isa => HashRef[InstanceOf['Gitpan::CPAN::Author']], 14 | lazy => 1, 15 | builder => '_build_authors'; 16 | 17 | haz file => 18 | is => 'ro', 19 | isa => Path, 20 | required => 1; 21 | 22 | method _build_authors { 23 | require IO::Zlib; 24 | my $fh = IO::Zlib->new; 25 | $fh->open( $self->file.'', 'rb' ) 26 | or croak "IO::Zlib can't open @{[$self->file]}: $!"; 27 | 28 | my $authors = {}; 29 | 30 | while( <$fh> ) { 31 | chomp; 32 | my($cpanid, $email, $name, $url) = split /\t/, Encode::decode_utf8($_); 33 | 34 | # Fall back to the author's cpan.org email if none is provided. 35 | $email = lc($cpanid).'@cpan.org' unless $self->is_valid_email($email); 36 | $url //= ''; 37 | 38 | $authors->{$cpanid} = Gitpan::CPAN::Author->new( 39 | cpanid => $cpanid, 40 | name => $name, 41 | url => $url, 42 | email => $email 43 | ); 44 | } 45 | 46 | return $authors; 47 | } 48 | 49 | 50 | method author(Str $cpanid) { 51 | return $self->__authors->{$cpanid}; 52 | } 53 | 54 | 55 | method is_valid_email(Str $email) { 56 | return 0 if !Email::Valid->address($email); 57 | return 0 if $email =~ /[<>]/; # git does not like angle brackets 58 | return 1; 59 | } 60 | -------------------------------------------------------------------------------- /lib/Gitpan/Config.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Config; 2 | 3 | use Gitpan::OO; 4 | use Gitpan::Types; 5 | use Gitpan::perl5i; 6 | 7 | haz backpan_url => 8 | is => 'ro', 9 | isa => URI, 10 | default => 'http://gitpan.integra.net/backpan'; 11 | 12 | haz backpan_index_url => 13 | is => 'ro', 14 | isa => URI, 15 | default => 'http://gitpan.integra.net/backpan-index.gz'; 16 | 17 | haz backpan_cache_dir => 18 | is => 'ro', 19 | isa => Maybe[AbsPath]; 20 | 21 | haz backpan_cache_ttl => 22 | is => 'ro', 23 | isa => Int, 24 | default => 60 * 60; 25 | 26 | haz backpan_always_update => 27 | is => 'ro', 28 | isa => Bool, 29 | default => 0; 30 | 31 | haz backpan_normalize_dist_names => 32 | is => 'ro', 33 | isa => HashRef, 34 | default => method { {} }; 35 | 36 | haz backpan_normalize_releases => 37 | is => 'ro', 38 | isa => HashRef[HashRef[Str]], 39 | default => method { {} }; 40 | 41 | haz skip => 42 | is => 'ro', 43 | isa => HashRef[ArrayRef], 44 | default => method { {} }; 45 | 46 | haz _skip_dists => 47 | is => 'ro', 48 | isa => HashRef, 49 | lazy => 1, 50 | clearer => '_clear_skip_dists', 51 | default => method { 52 | return scalar $self->skip->{dists}->as_hash; 53 | }; 54 | 55 | haz _skip_releases => 56 | is => 'ro', 57 | isa => HashRef, 58 | lazy => 1, 59 | clearer => '_clear_skip_releases', 60 | default => method { 61 | return scalar $self->skip->{releases}->as_hash; 62 | }; 63 | 64 | haz committer_email => 65 | is => 'ro', 66 | isa => Str, 67 | default => 'schwern+gitpan@pobox.com'; 68 | 69 | haz committer_name => 70 | is => 'ro', 71 | isa => Str, 72 | default => 'Gitpan'; 73 | 74 | haz "github_owner" => 75 | is => 'ro', 76 | isa => Str, 77 | default => 'gitpan'; 78 | 79 | haz "github_access_token" => 80 | is => 'ro', 81 | isa => Str, 82 | default => sub { 83 | return $ENV{GITPAN_GITHUB_ACCESS_TOKEN} || 84 | # A read only token for testing 85 | "f58a7dfa0f749ccb521c8da38f9649e2eff2434f" 86 | }; 87 | 88 | haz "github_remote_host" => 89 | is => 'ro', 90 | isa => Str, 91 | default => 'github.com'; 92 | 93 | haz "gitpan_dir" => 94 | is => 'ro', 95 | isa => Path, 96 | default => sub { 97 | return "$ENV{HOME}/gitpan"->path; 98 | }; 99 | 100 | haz "gitpan_latest_imported_release_timestamp" => 101 | is => 'ro', 102 | isa => Path, 103 | default => method { 104 | return $self->gitpan_dir->child(".latest_imported_release_timestamp"); 105 | }; 106 | 107 | haz "gitpan_log_dir" => 108 | is => 'ro', 109 | isa => Path, 110 | lazy => 1, 111 | default => method { 112 | return $self->gitpan_dir->child("log"); 113 | }; 114 | 115 | haz "gitpan_log_file" => 116 | is => 'ro', 117 | isa => Path, 118 | lazy => 1, 119 | default => method { 120 | return $self->gitpan_log_dir->child("gitpan.log"); 121 | }; 122 | 123 | haz "gitpan_repo_dir" => 124 | is => 'ro', 125 | isa => Path, 126 | lazy => 1, 127 | default => method { 128 | return $self->gitpan_dir->child("repo"); 129 | }; 130 | 131 | haz cpan_release_tag_prefix => 132 | is => 'ro', 133 | isa => Str, 134 | default => "cpan_version/"; 135 | 136 | haz gitpan_release_tag_prefix => 137 | is => 'ro', 138 | isa => Str, 139 | default => "gitpan_version/"; 140 | 141 | haz cpan_path_tag_prefix => 142 | is => 'ro', 143 | isa => Str, 144 | default => "cpan_path/"; 145 | 146 | 147 | { 148 | my $default; 149 | method default($class:) { 150 | require Gitpan::ConfigFile; 151 | return $default //= Gitpan::ConfigFile->default->config; 152 | } 153 | 154 | method set_default($class: Gitpan::Config $new_default) { 155 | $default = $new_default; 156 | return; 157 | } 158 | } 159 | 160 | 161 | method BUILD(...) { 162 | $self->gitpan_dir->mkpath; 163 | $self->gitpan_log_dir->mkpath; 164 | $self->gitpan_repo_dir->mkpath; 165 | 166 | return; 167 | } 168 | 169 | 170 | method skip_dist(Str $name) { 171 | return $self->_skip_dists->{$name}; 172 | } 173 | 174 | 175 | method skip_release(Str $name) { 176 | return $self->_skip_releases->{$name}; 177 | } 178 | 179 | 180 | =head1 NAME 181 | 182 | Gitpan::Config - Configuration of Gitpan 183 | 184 | =head1 DESCRIPTION 185 | 186 | Contains the configuration for Gitpan as read from a 187 | L. 188 | 189 | Gitpan classes should not hard code values or defaults, instead they 190 | should be used here. 191 | 192 | Gitpan classes should gain access to Gitpan::Config via 193 | L. 194 | 195 | =head2 Methods 196 | 197 | =head3 github_owner 198 | 199 | The Github account (owner) whose repositories we're accessing. 200 | 201 | Defaults to 'gitpan'. 202 | 203 | =head3 github_access_token 204 | 205 | The Github API access token used to access the github_owner's account. 206 | 207 | Defaults to the GITPAN_GITHUB_ACCESS_TOKEN environment variable or a 208 | read-only token for testing purposes. 209 | 210 | =head3 github_remote_host 211 | 212 | The remote host for Github. 213 | 214 | Defaults to github.com. 215 | 216 | =head1 ENVIRONMENT 217 | 218 | =head3 GITPAN_GITHUB_ACCESS_TOKEN 219 | 220 | See L. 221 | 222 | =head1 NOTES 223 | 224 | Configuration is very simple and flat right now. This may change once 225 | the configuration becomes more complicated. 226 | 227 | =cut 228 | -------------------------------------------------------------------------------- /lib/Gitpan/ConfigFile.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::ConfigFile; 2 | 3 | use Gitpan::perl5i; 4 | use Gitpan::OO; 5 | use Gitpan::Types; 6 | use Gitpan::Config; 7 | 8 | use YAML::XS qw(LoadFile); 9 | 10 | haz config_filename => 11 | is => 'ro', 12 | isa => Str, 13 | default => ".gitpan"; 14 | 15 | # Search from first to last. 16 | haz search_dirs => 17 | is => 'ro', 18 | isa => ArrayRef[Path], 19 | default => method { 20 | return [ 21 | map { $_->path } grep { defined && length } 22 | $ENV{GITPAN_CONFIG_DIR}, ".", $ENV{HOME} 23 | ]; 24 | }; 25 | 26 | haz config_file => 27 | is => 'ro', 28 | isa => Maybe[Path], 29 | lazy => 1, 30 | builder => 'search_for_config_file'; 31 | 32 | haz config => 33 | is => 'ro', 34 | isa => InstanceOf['Gitpan::Config'], 35 | lazy => 1, 36 | builder => 'read_config_file'; 37 | 38 | haz is_test => 39 | is => 'ro', 40 | isa => Bool, 41 | default => method { 42 | return $ENV{GITPAN_TEST} ? 1 : 0; 43 | }; 44 | 45 | haz use_overlays => 46 | is => 'ro', 47 | isa => ArrayRef, 48 | lazy => 1, 49 | default => method { 50 | return $self->is_test ? ["test"] : []; 51 | }; 52 | 53 | 54 | { 55 | my $default; 56 | method default($class:) { 57 | return $default //= Gitpan::ConfigFile->new; 58 | } 59 | 60 | method set_default($class: Gitpan::ConfigFile $new_default) { 61 | $default = $new_default; 62 | return; 63 | } 64 | } 65 | 66 | 67 | =head1 NAME 68 | 69 | Gitpan::ConfigFile - Configuration file for Gitpan 70 | 71 | =head1 SYNOPSIS 72 | 73 | # A ConfigFile object should generally not be created directly, 74 | # but via Gitpan::Role::HasConfig like so. 75 | my $config = $object->config; 76 | 77 | # Get some data out of the config. 78 | my $github_token = $config->config->{github}{token}; 79 | 80 | =head1 DESCRIPTION 81 | 82 | This is an object to access the Gitpan configuration. 83 | 84 | By default, the configuration file is stored in F<.gitpan>. It is 85 | looked for in GITPAN_CONFIG_DIR (environment variable), the current 86 | working directory or the home directory in that order. 87 | 88 | The format of the config file is YAML. 89 | 90 | =head2 Overlays 91 | 92 | Sometimes you want to change some config values in certain situations, 93 | such as when testing, without duplicating everything. For this there 94 | are "overlays". Values in an overlay will replace the normal values. 95 | 96 | Currently the only recognized overlay is "test" used while testing 97 | Gitpan. 98 | 99 | =head1 ENVIRONMENT 100 | 101 | =head3 GITPAN_CONFIG_DIR 102 | 103 | If set, it will look for the configuration file in this directory first. 104 | 105 | =head1 SEE ALSO 106 | 107 | L 108 | 109 | =cut 110 | 111 | 112 | method search_for_config_file { 113 | my $filename = $self->config_filename; 114 | my $dirs = $self->search_dirs; 115 | 116 | if( my $dir = $dirs->first(sub{ -e $_->path->child($filename) }) ) { 117 | return $dir->path->child($filename); 118 | } 119 | else { 120 | return; 121 | } 122 | } 123 | 124 | method read_config_file { 125 | my $config_data; 126 | if( my $file = $self->config_file ) { 127 | $config_data = $self->_apply_overlays( LoadFile( $file ) ); 128 | } 129 | else { 130 | $config_data = {}; 131 | } 132 | 133 | return Gitpan::Config->new($config_data); 134 | } 135 | 136 | method _apply_overlays( HashRef $config ) { 137 | # Don't want them in the final config. 138 | my $overlays = delete $config->{overlays}; 139 | 140 | $self->use_overlays->foreach( func($key) { 141 | my $overlay = $overlays->{$key}; 142 | return unless $overlay; 143 | 144 | $config = $config->merge($overlay); 145 | }); 146 | 147 | return $config; 148 | } 149 | -------------------------------------------------------------------------------- /lib/Gitpan/Dist.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Dist; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Gitpan::Release; 6 | use Gitpan::OO; 7 | use Gitpan::Types; 8 | 9 | use Gitpan::Repo; 10 | 11 | with 'Gitpan::Role::HasBackpanIndex'; 12 | 13 | use overload 14 | q[""] => method { return $self->distname }, 15 | fallback => 1; 16 | 17 | haz distname => 18 | is => 'ro', 19 | isa => DistName, 20 | default => method { 21 | $self->backpan_dist->name; 22 | }; 23 | 24 | with 'Gitpan::Role::CanDistLog'; 25 | 26 | haz backpan_dist => 27 | is => 'ro', 28 | isa => InstanceOf['BackPAN::Index::Dist'], 29 | lazy => 1, 30 | default => method { 31 | return $self->backpan_index->dist($self->distname); 32 | }; 33 | 34 | haz backpan_releases => 35 | is => 'ro', 36 | isa => InstanceOf["DBIx::Class::ResultSet"], 37 | lazy => 1, 38 | default => method { 39 | return scalar $self->backpan_dist->releases->search( 40 | # Ignore ppm releases, we only care about source releases. 41 | { path => { 'not like', '%.ppm' } }, 42 | # Import releases by date, don't try to figure out versions. 43 | { order_by => { -asc => "date" } } 44 | ); 45 | }; 46 | 47 | haz repo => 48 | is => 'ro', 49 | isa => InstanceOf['Gitpan::Repo'], 50 | handles => [qw( 51 | git 52 | has_git 53 | clear_git 54 | 55 | github 56 | has_github 57 | clear_github 58 | 59 | repo_dir 60 | delete_repo 61 | )], 62 | default => method { 63 | return Gitpan::Repo->new( 64 | distname => $self->distname 65 | ); 66 | }; 67 | 68 | 69 | method BUILDARGS($class: %args) { 70 | croak "distname or backpan_dist required" 71 | unless $args{distname} // $args{backpan_dist}; 72 | 73 | return \%args; 74 | } 75 | 76 | method release_from_version(Str $version) { 77 | return Gitpan::Release->new( 78 | distname => $self->distname, 79 | version => $version 80 | ); 81 | } 82 | 83 | method release_from_backpan( BackPAN::Index::Release $backpan_release ) { 84 | return Gitpan::Release->new( 85 | backpan_release => $backpan_release 86 | ); 87 | } 88 | 89 | 90 | method paths_to_import() { 91 | return [map { $_->short_path } @{$self->releases_to_import}]; 92 | } 93 | 94 | 95 | method versions_to_import() { 96 | return [map { $_->version } @{$self->releases_to_import}]; 97 | } 98 | 99 | method releases_to_import() { 100 | my $imported = $self->repo->releases->as_hash; 101 | 102 | my $config = $self->config; 103 | 104 | my @releases; 105 | for my $bp_release ($self->backpan_releases->all) { 106 | next if $imported->{$bp_release->short_path}; 107 | next if $config->skip_release($bp_release->short_path); 108 | 109 | push @releases, $self->release_from_backpan( $bp_release ); 110 | } 111 | 112 | return \@releases; 113 | } 114 | 115 | 116 | method import_releases(...) { 117 | $self->repo->import_releases( 118 | releases => $self->releases_to_import, 119 | @_ 120 | ); 121 | } 122 | -------------------------------------------------------------------------------- /lib/Gitpan/Github.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Github; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Gitpan::OO; 6 | use Gitpan::Types; 7 | use Pithub; 8 | use Encode; 9 | 10 | with 'Gitpan::Role::HasConfig', 'Gitpan::Role::CanBackoff'; 11 | 12 | method distname { return $self->pithub->repo } 13 | with "Gitpan::Role::CanDistLog"; 14 | 15 | haz "repo" => 16 | is => 'ro', 17 | required => 1; 18 | 19 | haz "owner" => 20 | is => 'ro', 21 | isa => Str, 22 | lazy => 1, 23 | default => method { 24 | return $self->config->github_owner; 25 | }; 26 | 27 | haz 'token' => 28 | is => 'ro', 29 | lazy => 1, 30 | default => method { 31 | return $self->config->github_access_token; 32 | }; 33 | 34 | haz pithub => 35 | is => 'ro', 36 | isa => InstanceOf['Pithub'], 37 | lazy => 1, 38 | default => method { 39 | return Pithub->new( 40 | user => $self->owner, 41 | repo => $self->repo_name_on_github, 42 | token => $self->token, 43 | per_page => 100, 44 | auto_pagination => 1, 45 | ); 46 | }; 47 | 48 | haz "repo_name_on_github" => 49 | is => 'ro', 50 | isa => Str, 51 | lazy => 1, 52 | default => method { 53 | my $repo = $self->repo; 54 | 55 | # Github doesn't like non alphanumerics as repository names. 56 | # Dots seem ok. 57 | $repo =~ s{[^a-z0-9-_.]+}{-}ig; 58 | 59 | # Maximum length is 100 characters. 60 | $repo = substr $repo, 0, 100; 61 | 62 | return $repo; 63 | }; 64 | 65 | haz "remote_host" => 66 | is => 'ro', 67 | isa => Str, 68 | lazy => 1, 69 | default => method { 70 | return $self->config->github_remote_host; 71 | }; 72 | 73 | haz "remote" => 74 | is => 'ro', 75 | isa => Str, 76 | lazy => 1, 77 | default => method { 78 | my $owner = $self->owner; 79 | my $repo = $self->repo_name_on_github; 80 | my $token = $self->token; 81 | my $host = $self->remote_host; 82 | 83 | return qq[https://$token:\@$host/$owner/$repo.git]; 84 | }; 85 | 86 | haz "_exists_on_github_cache" => 87 | is => 'rw', 88 | isa => Bool, 89 | default => 0; 90 | 91 | haz "retry_if_not_found" => 92 | is => "rw", 93 | isa => Bool, 94 | default => 0; 95 | 96 | method try_request( 97 | Int :$max_tries = 3, 98 | Bool :$retry_if_not_found //= $self->retry_if_not_found, 99 | CodeRef :$code!, 100 | CodeRef :$is_done = method($result) { 101 | return $result->success; 102 | }, 103 | CodeRef :$is_error = method($result) { 104 | my $code = $result->response->code; 105 | 106 | # We made a mistake in our request. 107 | # See https://developer.github.com/v3/#client-errors 108 | return(1) if $code == 400 || $code == 422; 109 | 110 | # Anything else should be retried. Might be a hiccup. 111 | return(0); 112 | }, 113 | CodeRef :$final_check = sub { 0 } 114 | ) { 115 | my $result; 116 | for my $tries (1..$max_tries) { 117 | $result = $code->(); 118 | return $result if $self->$is_done($result); 119 | 120 | $self->_try_request_error( 121 | result => $result 122 | ) if $self->$is_error($result); 123 | 124 | my $http_code = $result->response->code; 125 | return $result if !$retry_if_not_found && $http_code == 404; 126 | 127 | $self->backoff( tries => $tries, max_tries => $max_tries ); 128 | 129 | my $function = (caller(1))[3]; 130 | $self->dist_log( "Retrying $function: HTTP $http_code" ); 131 | } 132 | 133 | return $result if $self->$final_check($result); 134 | 135 | $self->_try_request_error( result => $result ); 136 | 137 | return; 138 | } 139 | 140 | 141 | method _try_request_error( 142 | Pithub::Result :$result!, 143 | ArrayRef :$caller = [caller(2)] 144 | ) { 145 | my $function = $caller->[3]; 146 | my $response = $result->response; 147 | croak <<"ERROR"; 148 | Error in $function, HTTP @{[$response->status_line]} 149 | @{[$result->mo->as_json]} 150 | ERROR 151 | 152 | return; 153 | } 154 | 155 | 156 | method get_repo_info( 157 | Bool :$retry_if_not_found //= $self->retry_if_not_found 158 | ) { 159 | my $repo = $self->repo; 160 | my $repo_on_github = $self->repo_name_on_github; 161 | 162 | $self->dist_log( "Getting Github repo info for $repo as $repo_on_github" ); 163 | 164 | my $result = $self->try_request( 165 | code => sub { return $self->pithub->repos->get }, 166 | retry_if_not_found => $retry_if_not_found, 167 | final_check => method($result) { 168 | return 1 if $result->response->code == 404; 169 | } 170 | ); 171 | 172 | return $result->content if $result->success; 173 | return; 174 | } 175 | 176 | method is_empty( 177 | Bool :$retry_if_not_found //= $self->retry_if_not_found 178 | ) { 179 | my $result = $self->try_request( 180 | code => sub { 181 | return $self->pithub->repos->commits(per_page => 1)->list; 182 | }, 183 | is_done => method($result) { 184 | return 1 if $result->success; 185 | 186 | # Github returns this if the repository is empty. 187 | return 1 if $result->response->code == 409; 188 | }, 189 | retry_if_not_found => $retry_if_not_found, 190 | ); 191 | 192 | return $result->count ? 0 : 1; 193 | } 194 | 195 | method exists_on_github(...) { 196 | $self->dist_log( "Checking if @{[ $self->repo ]} exists on Github" ); 197 | 198 | return 1 if $self->_exists_on_github_cache; 199 | 200 | my $info = $self->get_repo_info(@_); 201 | $self->_exists_on_github_cache(1) if $info; 202 | 203 | return $info ? 1 : 0; 204 | } 205 | 206 | method create_repo( 207 | :$desc //= "Read-only release history for @{[$self->repo]}", 208 | :$homepage //= "http://metacpan.org/release/@{[$self->repo]}", 209 | Bool :$retry_if_not_found //= $self->retry_if_not_found 210 | ) 211 | { 212 | my $repo = $self->repo; 213 | my $repo_name_on_github = $self->repo_name_on_github; 214 | 215 | $self->dist_log( "Creating Github repo for $repo as $repo_name_on_github" ); 216 | 217 | my $result = $self->try_request( 218 | code => sub { 219 | $self->pithub->repos->create( 220 | org => $self->owner, 221 | data => { 222 | name => encode_utf8($repo_name_on_github), 223 | description => encode_utf8($desc), 224 | homepage => encode_utf8($homepage), 225 | has_issues => 0, 226 | has_wiki => 0, 227 | } 228 | ); 229 | }, 230 | retry_if_not_found => $retry_if_not_found, 231 | ); 232 | 233 | my $created_name = $result->content->{name}; 234 | croak "Github repo name '$created_name' does not match our expected name '$repo_name_on_github'" if $created_name ne $repo_name_on_github; 235 | 236 | $self->_exists_on_github_cache(1); 237 | 238 | return $result; 239 | } 240 | 241 | method maybe_create( 242 | Str :$desc, 243 | Str :$homepage, 244 | Bool :$retry_if_not_found //= $self->retry_if_not_found 245 | ) 246 | { 247 | my $repo = $self->repo; 248 | 249 | return $repo if $self->exists_on_github; 250 | return $self->create_repo( 251 | desc => $desc, 252 | homepage => $homepage, 253 | retry_if_not_found => $retry_if_not_found, 254 | ); 255 | } 256 | 257 | method delete_repo_if_exists( 258 | Bool :$retry_if_not_found //= $self->retry_if_not_found 259 | ) { 260 | return if !$self->exists_on_github( 261 | retry_if_not_found => $retry_if_not_found, 262 | ); 263 | return $self->delete_repo( 264 | retry_if_not_found => $retry_if_not_found, 265 | ); 266 | } 267 | 268 | method default_success_check($result) { 269 | return 1 if $result->success; 270 | 271 | my $code = $result->response->code; 272 | my $message = $result->content->{message}; 273 | $self->dist_log( "HTTP $code, $message" ); 274 | 275 | croak "Unexpected HTTP code $code: $message" 276 | if $code == 400 or $code == 422; 277 | 278 | return 0; 279 | } 280 | 281 | method delete_repo( 282 | Bool :$retry_if_not_found //= $self->retry_if_not_found 283 | ) { 284 | my $repo = $self->repo; 285 | my $repo_on_github = $self->repo_name_on_github; 286 | 287 | $self->dist_log( "Deleting $repo on Github as $repo_on_github" ); 288 | 289 | my $result = $self->try_request( 290 | code => sub { $self->pithub->repos->delete; }, 291 | retry_if_not_found => $retry_if_not_found, 292 | ); 293 | 294 | $self->_exists_on_github_cache(0); 295 | 296 | return $result; 297 | } 298 | 299 | method branch_info( 300 | Str :$branch //= 'master', 301 | Bool :$retry_if_not_found //= $self->retry_if_not_found, 302 | ) { 303 | my $result = $self->try_request( 304 | max_tries => 6, 305 | code => sub { 306 | $self->dist_log("Trying to get info for $branch"); 307 | return $self->pithub->repos->branch( branch => $branch ); 308 | }, 309 | retry_if_not_found => $retry_if_not_found, 310 | ); 311 | 312 | return $result->content; 313 | } 314 | 315 | method change_repo_info(%changes) { 316 | my $retry_if_not_found = 317 | delete $changes{retry_if_not_found} // $self->retry_if_not_found; 318 | 319 | return 1 unless keys %changes; 320 | 321 | my $repo = $self->repo_name_on_github; 322 | 323 | # The Github API requires you send the name, even if you're not 324 | # changing it. This is silly. 325 | $changes{name} ||= $repo; 326 | 327 | my $log_changes = join ", ", map { "$_ => $changes{$_}" } keys %changes; 328 | $log_changes =~ s{\n}{\\n}g; 329 | $self->dist_log( "Changing @{[$self->repo]} (as $repo) info: $log_changes" ); 330 | 331 | my $result = $self->try_request( 332 | code => sub { 333 | $self->dist_log("Trying to change the repository"); 334 | return $self->pithub->repos->update( 335 | data => \%changes, 336 | ); 337 | }, 338 | retry_if_not_found => $retry_if_not_found 339 | ); 340 | 341 | return $result; 342 | } 343 | -------------------------------------------------------------------------------- /lib/Gitpan/OO.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::OO; 2 | 3 | use Gitpan::perl5i; 4 | 5 | require Moo; 6 | use Import::Into; 7 | 8 | sub import { 9 | my $class = shift; 10 | my $caller = caller; 11 | 12 | my $haz = func($name, %args) { 13 | $args{is} //= 'rw'; 14 | 15 | if( defined $args{isa} and $args{isa}->isa("Type::Tiny") ) { 16 | $args{coerce} = $args{isa}->coercion; 17 | } 18 | elsif (exists($args{coerce}) and not $args{coerce}) { 19 | delete($args{coerce}); 20 | } 21 | 22 | @_ = ($name, %args); 23 | goto "$caller"->can("has"); 24 | }; 25 | $haz->alias($caller.'::haz'); 26 | 27 | Moo->import::into($caller, @_); 28 | } 29 | -------------------------------------------------------------------------------- /lib/Gitpan/Release.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Release; 2 | 3 | use Gitpan::perl5i; 4 | use Gitpan::OO; 5 | use Gitpan::Types; 6 | 7 | with 8 | 'Gitpan::Role::HasBackpanIndex', 9 | 'Gitpan::Role::HasConfig', 10 | 'Gitpan::Role::HasCPANAuthors', 11 | 'Gitpan::Role::HasUA'; 12 | 13 | haz distname => 14 | is => 'ro', 15 | isa => DistName, 16 | default => method { 17 | return $self->backpan_release->dist->name; 18 | }; 19 | 20 | with 'Gitpan::Role::CanDistLog'; 21 | 22 | haz version => 23 | is => 'ro', 24 | isa => Str, 25 | default => method { 26 | return $self->backpan_release->version; 27 | }; 28 | 29 | haz gitpan_version => 30 | is => 'ro', 31 | isa => Str, 32 | lazy => 1, 33 | default => method { 34 | my $version = $self->version; 35 | $version =~ s{^v}{}; 36 | 37 | return $version; 38 | }; 39 | 40 | 41 | require BackPAN::Index::Release; 42 | # Fuck Type short_path() into BackPAN::Index::Release. 43 | *BackPAN::Index::Release::short_path = method { 44 | my $path = $self->path; 45 | $path =~ s{^authors/id/\w/\w{2}/}{}; 46 | return $path; 47 | }; 48 | 49 | 50 | haz backpan_release => 51 | is => 'ro', 52 | isa => InstanceOf['BackPAN::Index::Release'], 53 | lazy => 1, 54 | handles => [qw( 55 | cpanid 56 | date 57 | distvname 58 | filename 59 | maturity 60 | short_path 61 | )], 62 | default => method { 63 | return $self->backpan_index 64 | ->releases($self->distname) 65 | ->single({ version => $self->version }); 66 | }; 67 | 68 | haz url => 69 | is => 'ro', 70 | isa => URI, 71 | lazy => 1, 72 | default => method { 73 | my $url = $self->config->backpan_url->clone; 74 | $url->append_path($self->path); 75 | 76 | return $url; 77 | }; 78 | 79 | haz backpan_file => 80 | is => 'ro', 81 | isa => InstanceOf['BackPAN::Index::File'], 82 | lazy => 1, 83 | handles => [qw( 84 | path 85 | size 86 | )], 87 | default => method { 88 | $self->backpan_release->path; 89 | }; 90 | 91 | haz author => 92 | is => 'ro', 93 | isa => InstanceOf['Gitpan::CPAN::Author'], 94 | lazy => 1, 95 | default => method { 96 | return $self->cpan_authors->author($self->cpanid); 97 | }; 98 | 99 | haz work_dir => 100 | is => 'ro', 101 | isa => AbsPath, 102 | lazy => 1, 103 | default => method { 104 | require Path::Tiny; 105 | return Path::Tiny->tempdir; 106 | }; 107 | 108 | haz archive_file => 109 | is => 'rw', 110 | isa => AbsPath, 111 | lazy => 1, 112 | default => method { 113 | return $self->work_dir->child( $self->filename ); 114 | }; 115 | 116 | haz extract_dir => 117 | isa => AbsPath, 118 | clearer => "_clear_extract_dir"; 119 | 120 | haz github_file_size_limit => 121 | isa => Int, 122 | default => 100 * 1024 * 1024; 123 | 124 | method BUILDARGS($class: %args) { 125 | croak "distname & version or backpan_release required" 126 | unless ($args{distname} && defined $args{version}) || $args{backpan_release}; 127 | 128 | return \%args; 129 | } 130 | 131 | 132 | method get( 133 | Bool :$check_size = 1, 134 | Bool :$get_file_urls = 0 135 | ) { 136 | my $url = $self->url; 137 | 138 | $self->dist_log( "Getting $url" ); 139 | 140 | my $res; 141 | if( !$get_file_urls && $url->scheme eq 'file' ) { 142 | my $path = $url->path; 143 | croak "Could not find $path" unless -e $path; 144 | $self->archive_file($path); 145 | $res = HTTP::Response->new( 200, "file URL not copied" ); 146 | } 147 | else { 148 | $res = $self->ua->get( 149 | $url, 150 | ":content_file" => $self->archive_file."" 151 | ); 152 | } 153 | 154 | croak "Get from $url was not successful: ".$res->status_line 155 | unless $res->is_success; 156 | 157 | my $archive_size = -s $self->archive_file; 158 | croak "File not fully retrieved, got $archive_size, expected @{[$self->size]}" 159 | if $check_size && -s $self->archive_file != $self->size; 160 | 161 | return $res; 162 | } 163 | 164 | method extract { 165 | my $archive = $self->archive_file; 166 | my $dir = $self->work_dir; 167 | 168 | $self->dist_log( "Extracting $archive to $dir" ); 169 | 170 | croak "$archive does not exist, did you get it?" unless -e $archive; 171 | 172 | require Archive::Extract; 173 | local $Archive::Extract::PREFER_BIN = 1; 174 | my $ae = Archive::Extract->new( archive => $archive ); 175 | $ae->extract( to => $dir ) or 176 | croak "Couldn't extract $archive to $dir: ". $ae->error; 177 | 178 | croak "Archive is empty" if !$ae->extract_path; 179 | croak "Extraction directory does not exist" if !-e $ae->extract_path; 180 | 181 | $self->extract_dir( $ae->extract_path ); 182 | 183 | $self->fix_permissions; 184 | 185 | $self->fix_big_files; 186 | 187 | $self->fix_extract_dir($ae); 188 | 189 | return $self->extract_dir; 190 | } 191 | 192 | # Check for tarballs which unpack into cwd. Archive::Extract does not 193 | # eliminate the top level directory for us. 194 | method fix_extract_dir( Archive::Extract $ae ) { 195 | my $files = $ae->files; 196 | 197 | return unless $files->first(qr{^\./.+$}); 198 | 199 | my @children = $self->extract_dir->children; 200 | croak "Too many files in the extraction dir to fix it: @children" if @children > 1; 201 | 202 | my $child = $children[0]; 203 | CORE::system( "mv $child/* ".$self->extract_dir ) == 0 || 204 | croak "Could not move $child/* to ".$self->extract_dir; 205 | 206 | rmdir $child; 207 | 208 | return 1; 209 | } 210 | 211 | # Make sure the archive files are readable and the directories are traversable. 212 | method fix_permissions { 213 | return unless -d $self->extract_dir; 214 | 215 | $self->extract_dir->chmod("u+rx"); 216 | 217 | require File::Find; 218 | File::Find::find(sub { 219 | -d $_ ? $_->path->chmod("u+rx") : 220 | -f $_ ? $_->path->chmod("u+r") : 221 | 1; 222 | }, $self->extract_dir); 223 | 224 | return; 225 | } 226 | 227 | method fix_big_files() { 228 | return unless -d $self->extract_dir; 229 | 230 | my $limit = $self->github_file_size_limit; 231 | 232 | require File::Find; 233 | File::Find::find(sub { 234 | return if !-f $_; 235 | return if -s $_ < $limit; 236 | $self->truncate_file($File::Find::name); 237 | }, $self->extract_dir); 238 | } 239 | 240 | method truncate_file( $file ) { 241 | my $limit = ($self->github_file_size_limit / 1024 / 1024)->round; 242 | my $size = ((-s $file) / 1024 / 1024)->round; 243 | my $url = "http://backpan.cpan.org/".$self->path; 244 | 245 | $file->path->spew_utf8(<<"END"); 246 | Sorry, this file has been truncated by Gitpan. 247 | It was $size megs which exceeds Github's limit of $limit megs per file. 248 | You can get the file from the original archive at $url 249 | END 250 | 251 | return; 252 | } 253 | 254 | method move( 255 | Path::Tiny $to, 256 | Bool :$clean_for_import = 1 257 | ) { 258 | croak "$to is not a directory" if !-d $to; 259 | 260 | $self->extract if !$self->extract_dir; 261 | my $from = $self->extract_dir; 262 | 263 | $self->clean_extraction_for_import if $clean_for_import; 264 | 265 | $self->dist_log( "Moving from $from to $to" ); 266 | 267 | # Work around autodie failure. 268 | # "Internal error in Fatal/autodie. Leak-guard failure" 269 | CORE::system( "mv \Q$from\E/* \Q$to\E" ) && croak "mv failed: $!"; 270 | 271 | # Have to re-extract 272 | $self->_clear_extract_dir; 273 | 274 | return; 275 | } 276 | 277 | 278 | method clean_extraction_for_import() { 279 | my $dir = $self->extract_dir; 280 | 281 | # A .git directory in the tarball will interfere with 282 | # our own git repository. 283 | my $git_dir = $dir->child(".git"); 284 | if( -e $git_dir ) { 285 | $self->dist_log("Removing .git directory from the archive."); 286 | $git_dir->remove_tree({ safe => 0 }); 287 | } 288 | 289 | return; 290 | } 291 | -------------------------------------------------------------------------------- /lib/Gitpan/Repo.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Repo; 2 | 3 | # Handles the coordination of the Git and Gitpan repositories 4 | 5 | use Gitpan::perl5i; 6 | 7 | use Gitpan::OO; 8 | use Gitpan::Types; 9 | 10 | use Gitpan::Github; 11 | use Gitpan::Git; 12 | 13 | with 'Gitpan::Role::HasConfig', 14 | 'Gitpan::Role::CanBackoff'; 15 | 16 | haz distname => 17 | is => 'ro', 18 | isa => DistName, 19 | required => 1; 20 | 21 | with 'Gitpan::Role::CanDistLog'; 22 | 23 | haz repo_dir => 24 | isa => Path, 25 | lazy => 1, 26 | default => method { 27 | $self->config->gitpan_repo_dir->child($self->distname_path); 28 | }; 29 | 30 | haz github => 31 | is => 'ro', 32 | isa => InstanceOf['Gitpan::Github'], 33 | lazy => 1, 34 | predicate => 'has_github', 35 | clearer => 'clear_github', 36 | default => method { 37 | return Gitpan::Github->new( 38 | repo => $self->distname 39 | ); 40 | }; 41 | 42 | haz git => 43 | is => 'rw', 44 | isa => InstanceOf["Gitpan::Git"], 45 | lazy => 1, 46 | predicate => 'has_git', 47 | clearer => 'clear_git', 48 | default => method { 49 | return Gitpan::Git->new_or_init( 50 | repo_dir => $self->repo_dir, 51 | distname => $self->distname, 52 | ); 53 | }; 54 | 55 | haz is_prepared_for_commits => 56 | is => 'rw', 57 | isa => Bool, 58 | default => 0; 59 | 60 | haz is_prepared_for_push => 61 | is => 'rw', 62 | isa => Bool, 63 | default => 0; 64 | 65 | haz is_synced_with_github => 66 | is => 'rw', 67 | isa => Bool, 68 | default => 0; 69 | 70 | method default_success_check($return?) { 71 | return $return ? 1 : 0; 72 | } 73 | 74 | 75 | method delete_repo( Bool :$wait = 0 ) { 76 | $self->dist_log("Deleting repository @{[$self->distname]}"); 77 | 78 | $self->github->delete_repo_if_exists; 79 | 80 | # The ->git accessor will recreate the Github repo and clone it. 81 | # Avoid that. 82 | if( $self->have_git_repo ) { 83 | my $git = Gitpan::Git->new( 84 | repo_dir => $self->repo_dir, 85 | distname => $self->distname, 86 | ); 87 | $git->delete_repo; 88 | } 89 | 90 | # ->git may contain a now bogus object, kill it so the Repo object 91 | # can get a fresh git repo and still be useful. 92 | $self->clear_git; 93 | 94 | $self->wait_until_deleted if $wait; 95 | 96 | return 1; 97 | } 98 | 99 | 100 | method wait_until_deleted() { 101 | croak "Repo was not deleted in time" 102 | if $self->github->exists_on_github( retry_if_not_found => 0 ); 103 | 104 | return 1; 105 | } 106 | 107 | 108 | method wait_until_created() { 109 | croak "Repo was not created in time" 110 | if !$self->github->exists_on_github( retry_if_not_found => 1 ); 111 | 112 | return 1; 113 | } 114 | 115 | 116 | method sync_with_github() { 117 | return 1 if $self->is_synced_with_github; 118 | 119 | # An optimization for the most common case 120 | if( $self->have_git_repo && 121 | $self->git->remote("origin") eq $self->github->remote 122 | ) { 123 | my $git = Gitpan::Git->new( 124 | repo_dir => $self->repo_dir, 125 | distname => $self->distname 126 | ); 127 | $git->prepare_for_commits; 128 | $self->git($git); 129 | $git->pull( "ff_only" => 1 ); 130 | 131 | $self->is_prepared_for_commits(1); 132 | $self->is_prepared_for_push(1); 133 | } 134 | elsif( $self->have_github_repo ) { 135 | # We have a Git repo, update it from Github 136 | if( $self->have_git_repo ) { 137 | my $git = Gitpan::Git->new( 138 | repo_dir => $self->repo_dir, 139 | distname => $self->distname 140 | ); 141 | $git->prepare_for_commits; 142 | $self->git($git); 143 | 144 | $git->change_remote( origin => $self->github->remote ); 145 | $git->pull( "ff_only" => 1 ); 146 | } 147 | # No local repo, clone Github 148 | else { 149 | my $git = Gitpan::Git->clone( 150 | repo_dir => $self->repo_dir, 151 | distname => $self->distname, 152 | url => $self->github->remote 153 | ); 154 | $self->git($git); 155 | } 156 | 157 | $self->is_prepared_for_commits(1); 158 | $self->is_prepared_for_push(1); 159 | } 160 | 161 | $self->is_synced_with_github(1); 162 | 163 | return 1; 164 | } 165 | 166 | 167 | method prepare_for_push() { 168 | return 1 if $self->is_prepared_for_push; 169 | 170 | $self->dist_log("Repo prepare_for_push"); 171 | 172 | my $github = $self->github; 173 | 174 | if( $self->have_github_repo ) { 175 | $self->sync_with_github(); 176 | } 177 | # No Github repo, make one. 178 | else { 179 | $github->create_repo; 180 | 181 | # Init a repo, or use an existing one, 182 | # and set the remote to the new Github repo. 183 | # Save the cost of cloning nothing. 184 | my $git = Gitpan::Git->new_or_init( 185 | repo_dir => $self->repo_dir, 186 | distname => $self->distname 187 | ); 188 | $git->change_remote( origin => $self->github->remote ); 189 | $self->git($git); 190 | } 191 | 192 | $self->is_prepared_for_commits(1); 193 | $self->is_prepared_for_push(1); 194 | 195 | return 1; 196 | } 197 | 198 | 199 | method prepare_for_commits() { 200 | return 1 if $self->is_prepared_for_commits; 201 | 202 | $self->dist_log("Repo prepare_for_commits"); 203 | 204 | # There's a Github repo 205 | if( $self->have_github_repo ) { 206 | $self->sync_with_github; 207 | } 208 | # There's no Github repo, but there is a Git repo 209 | elsif( $self->have_git_repo ) { 210 | my $git = Gitpan::Git->new( 211 | repo_dir => $self->repo_dir, 212 | distname => $self->distname 213 | ); 214 | $git->prepare_for_commits; 215 | $self->git($git); 216 | } 217 | # There's no Git or Github repo 218 | else { 219 | my $git = Gitpan::Git->init( 220 | repo_dir => $self->repo_dir, 221 | distname => $self->distname 222 | ); 223 | $self->git($git); 224 | } 225 | 226 | $self->is_prepared_for_commits(1); 227 | 228 | return 1; 229 | } 230 | 231 | 232 | method have_git_repo() { 233 | return -d $self->repo_dir->child(".git"); 234 | } 235 | 236 | 237 | method have_github_repo() { 238 | return $self->github->exists_on_github; 239 | } 240 | 241 | 242 | method releases() { 243 | $self->sync_with_github; 244 | 245 | return [] if !$self->have_git_repo; 246 | return $self->git->releases; 247 | } 248 | 249 | 250 | method are_git_and_github_on_the_same_commit() { 251 | my $branch_info = $self->github->branch_info( retry_if_not_found => 1 ); 252 | return $self->git->head->target->id eq $branch_info->{commit}{sha}; 253 | } 254 | 255 | method push(...) { 256 | $self->prepare_for_push; 257 | return $self->git->push(@_); 258 | } 259 | 260 | method pull(...) { 261 | return $self->git->pull(@_); 262 | } 263 | 264 | 265 | method import_release( 266 | Gitpan::Release $release, 267 | Bool :$push = 0, 268 | Bool :$clean = 0 269 | ) { 270 | # Capture and log warnings, prepending with the specific release. 271 | local $SIG{__WARN__} = sub { 272 | $self->main_log("@{[$release->short_path]}: $_") for @_; 273 | $self->dist_log(join "", @_); 274 | }; 275 | 276 | $self->main_log( "Importing @{[$release->short_path]}" ); 277 | $self->dist_log( "Importing @{[$release->short_path]}" ); 278 | 279 | $self->prepare_for_commits; 280 | 281 | my $git = $self->git; 282 | 283 | $release->get; 284 | 285 | $git->rm_all; 286 | 287 | $release->move($git->repo_dir); 288 | 289 | $git->add_all; 290 | 291 | $git->commit_release($release); 292 | 293 | $git->clean if $clean; 294 | $self->push if $push; 295 | 296 | return 1; 297 | } 298 | 299 | 300 | method import_releases( 301 | ArrayRef[Gitpan::Release] :$releases, 302 | CodeRef :$before_import = sub {}, 303 | CodeRef :$after_import = sub {}, 304 | Bool :$push = 1, 305 | Bool :$clean = 1 306 | ) { 307 | if( !@$releases ) { 308 | $self->main_log( "Nothing to import for @{[$self->distname]}" ); 309 | return; 310 | } 311 | 312 | # Capture and log warnings. 313 | local $SIG{__WARN__} = sub { 314 | $self->main_log("@{[$self->distname]}: $_") for @_; 315 | $self->dist_log(join "", @_); 316 | }; 317 | 318 | # Check if a repository with the same name, but different casing, already 319 | # exists on Github. 320 | my $github_repo = $self->github->get_repo_info; 321 | if( $github_repo && $github_repo->{name} ne $self->github->repo_name_on_github ) { 322 | $self->main_log("Error: distribution $github_repo->{name} already exists, @{[$self->distname]} would clash."); 323 | return 0; 324 | } 325 | 326 | my $versions = join ", ", map { $_->version } @$releases; 327 | $self->main_log( "Importing @{[$self->distname]} versions $versions" ); 328 | $self->dist_log( "Importing $versions" ); 329 | 330 | for my $release (@$releases) { 331 | eval { 332 | $self->$before_import($release); 333 | $self->import_release($release); 334 | $self->$after_import($release); 335 | 1; 336 | } or do { 337 | $self->main_log("Error importing @{[$release->short_path]}: $@"); 338 | $self->dist_log("$@"); 339 | }; 340 | } 341 | 342 | $self->git->clean if $clean; 343 | $self->push if $push; 344 | 345 | return 1; 346 | } 347 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/CanBackoff.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::CanBackoff; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Moo::Role; 6 | requires "default_success_check"; 7 | 8 | use Gitpan::Types; 9 | 10 | use Time::HiRes qw(sleep); 11 | 12 | method do_with_backoff(Int :$times=4, CodeRef :$code!, CodeRef :$check) { 13 | $check //= $self->can("default_success_check"); 14 | 15 | for my $time (1..$times) { 16 | my $return = $code->(); 17 | return $return if $self->$check($return); 18 | 19 | $self->backoff(tries => $time, max_tries => $times); 20 | } 21 | 22 | return; 23 | } 24 | 25 | 26 | method backoff( 27 | Int :$tries!, 28 | Int :$max_tries 29 | ) { 30 | # Infinity is not recognized as an integer 31 | $max_tries //= "Inf"; 32 | 33 | # .5 1 2 4 8 ... 34 | sleep(2**($tries-1)/2) if $tries < $max_tries; 35 | 36 | return; 37 | } 38 | 39 | 1; 40 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/CanDistLog.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::CanDistLog; 2 | 3 | use Gitpan::perl5i; 4 | use Moo::Role; 5 | use Gitpan::Types; 6 | 7 | use namespace::clean; 8 | 9 | with "Gitpan::Role::CanLog"; 10 | 11 | requires "config", "distname"; 12 | 13 | method dist_log_file { 14 | $self->config->gitpan_log_dir->child($self->distname_path); 15 | } 16 | 17 | method dist_log($message) { 18 | return $self->_log( 19 | file => $self->dist_log_file, 20 | message => $message 21 | ); 22 | } 23 | 24 | method distname_path() { 25 | my $name = $self->distname; 26 | my @path = ( 27 | uc $name->substr(0, 2) || "--", 28 | $name 29 | ); 30 | 31 | require Path::Tiny; 32 | return Path::Tiny->new(@path); 33 | } 34 | 35 | method BUILD(...) { 36 | $self->dist_log_file->touchpath if !-e $self->dist_log_file; 37 | 38 | return; 39 | } 40 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/CanLog.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::CanLog; 2 | 3 | use Gitpan::perl5i; 4 | use Moo::Role; 5 | use Gitpan::Types; 6 | 7 | use namespace::clean; 8 | 9 | requires "config"; 10 | 11 | method _log(Path::Tiny :$file, Str|Object :$message) { 12 | $message .= "\n" unless $message =~ m{\n$}; 13 | 14 | local $@; 15 | return $file->append_utf8($message); 16 | } 17 | 18 | method main_log($message) { 19 | return $self->_log( 20 | file => $self->config->gitpan_log_file, 21 | message => $message 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/HasBackpanIndex.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::HasBackpanIndex; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Moo::Role; 6 | use Gitpan::Types; 7 | 8 | with "Gitpan::Role::HasConfig"; 9 | 10 | has backpan_index => 11 | is => 'rw', 12 | isa => InstanceOf['BackPAN::Index'], 13 | lazy => 1, 14 | builder => "default_backpan_index"; 15 | 16 | # Everybody share one index object. 17 | method default_backpan_index { 18 | require BackPAN::Index; 19 | 20 | state $index; 21 | state $pid; 22 | 23 | # We can't pass active SQLite connections to the child. 24 | # Make a fresh BackPAN::Index object with a fresh connection. 25 | $index = do { 26 | $pid = $$; 27 | $self->_build_index; 28 | } if !$index || $pid != $$; 29 | 30 | return $index; 31 | } 32 | 33 | method _build_index { 34 | my $config = $self->config; 35 | 36 | my @opts; 37 | push @opts, (update => 1) if $config->backpan_always_update; 38 | 39 | push @opts, (normalize_dist_names => $config->backpan_normalize_dist_names) 40 | if keys %{$config->backpan_normalize_dist_names}; 41 | push @opts, (normalize_releases => $config->backpan_normalize_releases) 42 | if keys %{$config->backpan_normalize_releases}; 43 | 44 | push @opts, (cache_dir => $config->backpan_cache_dir.'') 45 | if defined $config->backpan_cache_dir; 46 | 47 | push @opts, (backpan_index_url => $config->backpan_index_url) 48 | if defined $config->backpan_index_url; 49 | 50 | return BackPAN::Index->new( 51 | cache_ttl => $self->config->backpan_cache_ttl, 52 | releases_only_from_authors => 1, 53 | @opts 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/HasCPANAuthors.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::HasCPANAuthors; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Carp; 6 | use Moo::Role; 7 | use Gitpan::Types; 8 | 9 | use Gitpan::CPAN::Authors; 10 | 11 | with "Gitpan::Role::HasUA", 12 | "Gitpan::Role::HasConfig"; 13 | 14 | has cpan_authors => 15 | is => 'rw', 16 | isa => InstanceOf['Gitpan::CPAN::Authors'], 17 | lazy => 1, 18 | builder => 'build_cpan_authors'; 19 | 20 | method build_cpan_authors { 21 | state $authors; 22 | return $authors if $authors; 23 | 24 | my $authors_url = $self->config->backpan_url->clone; 25 | $authors_url->append_path("authors/02authors.txt.gz"); 26 | 27 | my $authors_file = Path::Tiny->tempfile; 28 | my $res = $self->ua->get( 29 | $authors_url, 30 | ":content_file" => $authors_file.'' 31 | ); 32 | croak "Get from @{[$authors_url]} was not successful: ".$res->status_line 33 | unless $res->is_success; 34 | 35 | $authors = Gitpan::CPAN::Authors->new( 36 | file => $authors_file 37 | ); 38 | 39 | return $authors; 40 | } 41 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/HasConfig.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::HasConfig; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Moo::Role; 6 | use Gitpan::Types; 7 | 8 | use Gitpan::Config; 9 | 10 | method config() { 11 | return Gitpan::Config->default; 12 | } 13 | 14 | 15 | =head1 NAME 16 | 17 | Gitpan::Role::HasConfig - Per object access to the config 18 | 19 | =head1 SYNOPSIS 20 | 21 | { 22 | package Some::Class; 23 | use Gitpan::OO; 24 | with 'Gitpan::Role::HasConfig'; 25 | } 26 | 27 | my $obj = Some::Class->new; 28 | my $config = $obj->config; 29 | 30 | =head1 DESCRIPTION 31 | 32 | With this role your object will have access to the Gitpan configuration. 33 | 34 | The configuration is shared by all. 35 | 36 | =head2 Methods 37 | 38 | =head3 config 39 | 40 | Returns the shared L object. 41 | 42 | This can be called as a class or object method. 43 | 44 | =head1 SEE ALSO 45 | 46 | L 47 | 48 | =cut 49 | -------------------------------------------------------------------------------- /lib/Gitpan/Role/HasUA.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Role::HasUA; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Moo::Role; 6 | use Gitpan::Types; 7 | 8 | has ua => 9 | is => 'rw', 10 | isa => InstanceOf['LWP::UserAgent'], 11 | lazy => 1, 12 | builder => 'default_ua'; 13 | 14 | # Everybody share one index object. 15 | method default_ua { 16 | require LWP::UserAgent; 17 | state $lwp = LWP::UserAgent->new; 18 | return $lwp; 19 | } 20 | -------------------------------------------------------------------------------- /lib/Gitpan/Types.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Types; 2 | 3 | use Gitpan::perl5i; 4 | 5 | use Type::Library -base; 6 | use Type::Utils -all; 7 | BEGIN { extends "Types::Standard" } 8 | 9 | sub import { 10 | # Export :types by default. 11 | push @_, ":types" if @_ == 1; 12 | goto __PACKAGE__->can("SUPER::import"); 13 | } 14 | 15 | class_type "BackPAN::Index"; 16 | class_type "Gitpan::Dist"; 17 | class_type "Gitpan::Repo"; 18 | 19 | 20 | declare "Path", 21 | as InstanceOf["Path::Tiny"]; 22 | 23 | coerce "Path", 24 | from Str, 25 | via { 26 | return $_->path; 27 | }; 28 | 29 | 30 | declare "AbsPath", 31 | as "Path", 32 | where { $_->is_absolute }; 33 | 34 | coerce "AbsPath", 35 | from "Path", 36 | via { 37 | return $_->absolute; 38 | }; 39 | 40 | coerce "AbsPath", 41 | from Str, 42 | via { 43 | return $_->path->absolute; 44 | }; 45 | 46 | 47 | declare "DistName", 48 | as Str, 49 | message { "A CPAN distribution name" }, 50 | where { length and !/\s/ and !/::/ }; 51 | 52 | 53 | declare "ModuleName", 54 | as Str, 55 | message { "A CPAN module name " }, 56 | where { /^[A-Za-z]+ (?: :: \w+)* /x }; 57 | 58 | 59 | declare "URI", 60 | as InstanceOf["URI"]; 61 | 62 | coerce "URI", 63 | from Str, 64 | via { 65 | require URI; 66 | return URI->new($_); 67 | }; 68 | 69 | # Fuck Typing 70 | sub URI::append_path { 71 | my $self = shift; 72 | my $to_append = shift; 73 | 74 | my $path = $self->path; 75 | $path =~ s{/$}{}; 76 | $to_append =~ s{^/}{}; 77 | 78 | $self->path($path ."/". $to_append); 79 | 80 | return $self; 81 | } 82 | -------------------------------------------------------------------------------- /lib/Gitpan/perl5i.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::perl5i; 2 | 3 | use perl5i::2 (); 4 | use Method::Signatures (); 5 | 6 | use Import::Into; 7 | 8 | sub import { 9 | my $caller = caller; 10 | perl5i::2->import::into($caller, "-skip" => ["Signatures"]); 11 | Method::Signatures->import::into($caller); 12 | } 13 | 14 | 1; 15 | -------------------------------------------------------------------------------- /src/BackPAN-Index-0.42_02.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/src/BackPAN-Index-0.42_02.tar.gz -------------------------------------------------------------------------------- /t/CanBackoff.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | note "Setup class for testing"; { 8 | package Foo; 9 | use Gitpan::OO; 10 | with "Gitpan::Role::CanBackoff"; 11 | 12 | sub default_success_check { 13 | my $self = shift; 14 | my $response = shift; 15 | return $response ? 1 : 0; 16 | } 17 | } 18 | 19 | subtest "backoff()" => sub { 20 | my $obj = Foo->new; 21 | 22 | test_runtime( 23 | code => sub { $obj->backoff( tries => 3 ) }, 24 | time => 2 25 | ); 26 | 27 | test_runtime( 28 | code => sub { $obj->backoff( tries => 2, max_tries => 2 ) }, 29 | time => 0 30 | ); 31 | 32 | test_runtime( 33 | code => sub { $obj->backoff( tries => 2, max_tries => 1 ) }, 34 | time => 0 35 | ); 36 | 37 | test_runtime( 38 | code => sub { $obj->backoff( tries => 2, max_tries => 3 ) }, 39 | time => 1 40 | ); 41 | }; 42 | 43 | subtest "do_with_backoff()" => sub { 44 | my $obj = Foo->new; 45 | 46 | my $i = 0; 47 | is $obj->do_with_backoff( 48 | times => 5, 49 | code => sub { ++$i }, 50 | check => sub { $_[1] > 2 } 51 | ), 3; 52 | 53 | ok !$obj->do_with_backoff( 54 | times => 2, 55 | code => sub { 42 }, 56 | check => sub { 0 } 57 | ); 58 | 59 | $i = 0; 60 | ok $obj->do_with_backoff( times => 2, code => sub { $i++ } ); 61 | }; 62 | 63 | 64 | subtest "do_with_backoff timing" => sub { 65 | my $obj = Foo->new; 66 | 67 | test_runtime( 68 | code => sub { $obj->do_with_backoff( times => 4, code => sub { 0 } ) }, 69 | time => 3.5 70 | ); 71 | }; 72 | 73 | done_testing; 74 | -------------------------------------------------------------------------------- /t/CanDistLog.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | subtest "Setup class" => sub { 8 | package Foo; 9 | use Gitpan::OO; 10 | use Gitpan::Types; 11 | 12 | haz distname => 13 | isa => Str, 14 | required => 1; 15 | 16 | with 17 | "Gitpan::Role::CanDistLog", 18 | "Gitpan::Role::HasConfig"; 19 | 20 | ::pass; 21 | }; 22 | 23 | 24 | subtest "distname_path" => sub { 25 | my $obj = Foo->new( distname => "F-B-D" ); 26 | is $obj->distname_path, "F-/F-B-D"; 27 | 28 | $obj = Foo->new( distname => "F" ); 29 | is $obj->distname_path, "F/F"; 30 | 31 | $obj = Foo->new( distname => "acme-pony" ); 32 | is $obj->distname_path, "AC/acme-pony"; 33 | }; 34 | 35 | 36 | subtest "dist_log" => sub { 37 | my $obj = Foo->new( distname => "Foo-Bar" ); 38 | $obj->dist_log("something"); 39 | $obj->dist_log("and something\n"); 40 | $obj->dist_log("with ünicode"); 41 | 42 | like $obj->dist_log_file, qr{FO/Foo-Bar}; 43 | 44 | is $obj->dist_log_file->slurp_utf8, <<'END'; 45 | something 46 | and something 47 | with ünicode 48 | END 49 | }; 50 | 51 | 52 | done_testing; 53 | 54 | -------------------------------------------------------------------------------- /t/CanLog.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | subtest "Setup class" => sub { 8 | package Foo; 9 | use Gitpan::OO; 10 | with 11 | "Gitpan::Role::CanLog", 12 | "Gitpan::Role::HasConfig"; 13 | 14 | ::pass; 15 | }; 16 | 17 | 18 | subtest "main_log" => sub { 19 | my $obj = Foo->new; 20 | $obj->main_log("something"); 21 | $obj->main_log("and something\n"); 22 | $obj->main_log("with ünicode"); 23 | 24 | is $obj->config->gitpan_log_file->slurp_utf8, <<'END'; 25 | something 26 | and something 27 | with ünicode 28 | END 29 | $obj->config->gitpan_log_file->remove; 30 | }; 31 | 32 | 33 | subtest q[don't blow over $@] => sub { 34 | local $@ = 'foo'; 35 | 36 | my $obj = Foo->new; 37 | $obj->main_log("something"); 38 | is $@, 'foo'; 39 | 40 | $obj->config->gitpan_log_file->remove; 41 | }; 42 | 43 | subtest "main_log overloaded object" => sub { 44 | { 45 | package Bar; 46 | use Gitpan::OO; 47 | use overload 48 | q[""] => sub { "some message" }, 49 | fallback => 1; 50 | } 51 | 52 | my $obj = Foo->new; 53 | $obj->main_log(Bar->new); 54 | 55 | is $obj->config->gitpan_log_file->slurp_utf8, <<'END'; 56 | some message 57 | END 58 | }; 59 | 60 | 61 | done_testing; 62 | -------------------------------------------------------------------------------- /t/Config.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | my $CLASS = 'Gitpan::Config'; 8 | require_ok $CLASS; 9 | 10 | subtest "github defaults" => sub { 11 | local $ENV{GITPAN_GITHUB_ACCESS_TOKEN}; 12 | 13 | my $config = new_ok $CLASS; 14 | 15 | isa_ok $config->backpan_url, "URI"; 16 | 17 | is $config->github_owner, 'gitpan'; 18 | is $config->github_access_token, 'f58a7dfa0f749ccb521c8da38f9649e2eff2434f'; 19 | is $config->github_remote_host, 'github.com'; 20 | 21 | is $config->committer_name, 'Gitpan'; 22 | is $config->committer_email, 'schwern+gitpan@pobox.com'; 23 | 24 | is $config->gitpan_dir, "$ENV{HOME}/gitpan"; 25 | is $config->gitpan_log_dir, "$ENV{HOME}/gitpan/log"; 26 | is $config->gitpan_repo_dir, "$ENV{HOME}/gitpan/repo"; 27 | 28 | ok !$config->backpan_always_update; 29 | 30 | cmp_deeply $config->backpan_normalize_dist_names, {}; 31 | cmp_deeply $config->backpan_normalize_releases, {}; 32 | }; 33 | 34 | 35 | subtest default => sub { 36 | is $CLASS->default->mo->id, $CLASS->default->mo->id; 37 | 38 | my $new_default = $CLASS->new; 39 | $CLASS->set_default($new_default); 40 | 41 | is $CLASS->default->mo->id, $new_default->mo->id; 42 | }; 43 | 44 | 45 | subtest "github_access_token env" => sub { 46 | local $ENV{GITPAN_GITHUB_ACCESS_TOKEN} = 'deadbeef'; 47 | 48 | my $config = new_ok $CLASS; 49 | 50 | is $config->github_access_token, 'deadbeef'; 51 | }; 52 | 53 | 54 | subtest "setup directories" => sub { 55 | my $config = new_ok $CLASS; 56 | 57 | ok -d $config->gitpan_dir; 58 | ok -d $config->gitpan_log_dir; 59 | ok -d $config->gitpan_repo_dir; 60 | }; 61 | 62 | done_testing; 63 | -------------------------------------------------------------------------------- /t/ConfigFile.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Gitpan::perl5i; 4 | use Test::Most; 5 | use Path::Tiny; 6 | use YAML::XS qw(DumpFile); 7 | 8 | my $CLASS = 'Gitpan::ConfigFile'; 9 | require_ok $CLASS; 10 | 11 | 12 | # Test just the values in the Gitpan::Config object we care about, 13 | # ignore defaults and the like. 14 | func test_config($config, $want) { 15 | $want->each( func($method, $value) { 16 | is $config->$method, $value; 17 | }); 18 | } 19 | 20 | 21 | subtest default => sub { 22 | is $CLASS->default->mo->id, $CLASS->default->mo->id; 23 | 24 | my $new_default = $CLASS->new; 25 | $CLASS->set_default($new_default); 26 | 27 | is $CLASS->default->mo->id, $new_default->mo->id; 28 | }; 29 | 30 | 31 | subtest defaults => sub { 32 | my $config = new_ok $CLASS; 33 | 34 | ok !$config->is_test; 35 | is $config->config_filename, ".gitpan"; 36 | is_deeply $config->search_dirs, [".", $ENV{HOME}]; 37 | isa_ok $config->config, 'Gitpan::Config', "config should always return something"; 38 | is_deeply $config->use_overlays, []; 39 | } or BAIL_OUT("config didn't pass basic tests, this is bad"); 40 | 41 | 42 | subtest env_GITPAN_CONFIG_DIR => sub { 43 | my $tempdir = Path::Tiny->tempdir; 44 | my $config_file = $tempdir->child(".gitpan"); 45 | local $ENV{GITPAN_CONFIG_DIR} = $tempdir; 46 | 47 | my $config_data = { 48 | github_access_token => "fromenv", 49 | }; 50 | DumpFile($config_file, $config_data); 51 | 52 | my $config = new_ok $CLASS; 53 | test_config( $config->config, { github_access_token => "fromenv" } ); 54 | }; 55 | 56 | 57 | subtest read_config => sub { 58 | my $tempdir = Path::Tiny->tempdir; 59 | my $config_file = $tempdir->child("test.gitpan"); 60 | 61 | my $config_data = { 62 | github_access_token => "123abc", 63 | overlays => { foo => { github_access_token => "deadbeef" } } 64 | }; 65 | DumpFile($config_file, $config_data); 66 | 67 | my $config = new_ok $CLASS, [ 68 | config_filename => 'test.gitpan', 69 | search_dirs => [$tempdir], 70 | ]; 71 | 72 | is $config->config_file, $config_file; 73 | test_config( $config->config, { github_access_token => "123abc" } ); 74 | }; 75 | 76 | subtest overlays => sub { 77 | my $tempdir = Path::Tiny->tempdir; 78 | my $config_file = $tempdir->child("test.gitpan"); 79 | 80 | my $config_data = { 81 | github_access_token => "123abc", 82 | github_remote_host => "example.com", 83 | overlays => { test => { github_access_token => "deadbeef" } } 84 | }; 85 | DumpFile($config_file, $config_data); 86 | 87 | my $config = new_ok $CLASS, [ 88 | config_filename => 'test.gitpan', 89 | search_dirs => [$tempdir], 90 | is_test => 1, 91 | ]; 92 | 93 | is $config->config_file, $config_file; 94 | test_config( $config->config, { 95 | github_access_token => "deadbeef", 96 | github_remote_host => "example.com" 97 | }); 98 | }; 99 | 100 | done_testing; 101 | -------------------------------------------------------------------------------- /t/Dist.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | my $CLASS = 'Gitpan::Dist'; 8 | 9 | require_ok $CLASS; 10 | 11 | note "Required args"; { 12 | throws_ok { $CLASS->new } qr/^distname or backpan_dist required/; 13 | } 14 | 15 | note "The basics"; { 16 | my $dist = $CLASS->new( distname => "Acme-Pony" ); 17 | 18 | is $dist->backpan_dist->name, $dist->distname; 19 | 20 | my $releases = $dist->backpan_releases; 21 | cmp_ok $releases->count, ">=", 2; 22 | my $first_release = $releases->first; 23 | isa_ok $first_release, "BackPAN::Index::Release"; 24 | is $first_release->version, '1.1.1'; 25 | } 26 | 27 | 28 | note "new() from BackPAN::Index::Dist"; { 29 | my $bp_dist = $CLASS->new( distname => "Acme-Pony" )->backpan_dist; 30 | 31 | my $dist = $CLASS->new( backpan_dist => $bp_dist ); 32 | is $dist->distname, 'Acme-Pony'; 33 | } 34 | 35 | 36 | note "release_from_version"; { 37 | my $dist = $CLASS->new( distname => "Acme-Pony" ); 38 | 39 | my $release = $dist->release_from_version('1.1.1'); 40 | isa_ok $release, "Gitpan::Release"; 41 | is $release->distname, "Acme-Pony"; 42 | is $release->version, "1.1.1"; 43 | } 44 | 45 | 46 | note "release_from_backpan"; { 47 | my $dist = $CLASS->new( distname => "Acme-Buffy" ); 48 | 49 | # There are two releases for 1.3 50 | my @bp_releases = $dist->backpan_releases->search({ version => '1.3' })->all; 51 | for my $bp_release (@bp_releases) { 52 | my $release = Gitpan::Release->new( 53 | backpan_release => $bp_release 54 | ); 55 | is $release->version, '1.3'; 56 | is $release->distname, 'Acme-Buffy'; 57 | } 58 | } 59 | 60 | note "dist data"; { 61 | my $dist = $CLASS->new( distname => "Foo-Bar" ); 62 | isa_ok $dist, $CLASS; 63 | 64 | is $dist->distname, "Foo-Bar"; 65 | } 66 | 67 | 68 | note "releases to import"; { 69 | my $dist = new_dist( distname => 'Acme-LookOfDisapproval' ); 70 | 71 | # Releaes of Acme-LOD as of this writing. 72 | my @backpan_versions = (0.001, 0.002, 0.003, 0.004, 0.005, 0.006); 73 | my @backpan_paths = ( 74 | "ETHER/Acme-LookOfDisapproval-0.001.tar.gz", 75 | "ETHER/Acme-LookOfDisapproval-0.002.tar.gz", 76 | "ETHER/Acme-LookOfDisapproval-0.003.tar.gz", 77 | "ETHER/Acme-LookOfDisapproval-0.004.tar.gz", 78 | "ETHER/Acme-LookOfDisapproval-0.005.tar.gz", 79 | "ETHER/Acme-LookOfDisapproval-0.006.tar.gz", 80 | ); 81 | cmp_deeply scalar @backpan_versions->diff($dist->versions_to_import), []; 82 | cmp_deeply scalar @backpan_paths ->diff($dist->paths_to_import), []; 83 | 84 | my $git = $dist->git; 85 | $git->repo_dir->child("foo")->touch; 86 | $git->add_all; 87 | $git->commit( message => "Adding foo" ); 88 | $git->tag_release( $dist->release_from_version(0.001) ); 89 | 90 | cmp_deeply scalar @backpan_versions->diff($dist->versions_to_import), [0.001]; 91 | cmp_deeply scalar @backpan_paths->diff($dist->paths_to_import), 92 | ['ETHER/Acme-LookOfDisapproval-0.001.tar.gz']; 93 | } 94 | 95 | note "restarting from an existing repository"; { 96 | note "Import a release to establish the repository"; { 97 | my $dist = Gitpan::Dist->new( 98 | distname => 'Acme-LookOfDisapproval' 99 | ); 100 | $dist->delete_repo( wait => 1 ); 101 | $dist->repo->import_release( $dist->release_from_version(0.001), push => 1 ); 102 | } 103 | 104 | my $dist = Gitpan::Dist->new( 105 | distname => 'Acme-LookOfDisapproval' 106 | ); 107 | cmp_deeply $dist->repo->releases, ["ETHER/Acme-LookOfDisapproval-0.001.tar.gz"]; 108 | } 109 | 110 | done_testing; 111 | -------------------------------------------------------------------------------- /t/Dist/import_releases.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Dist; 8 | 9 | subtest "error handling" => sub { 10 | my $dist = new_dist( distname => "Acme-LookOfDisapproval" ); 11 | 12 | my $error_at = $dist->release_from_version(0.003); 13 | 14 | $dist->import_releases( 15 | before_import => method($release) { 16 | die "Testing error" if $release->version == $error_at->version; 17 | }, 18 | push => 0, 19 | ); 20 | 21 | like $dist->config->gitpan_log_file->slurp_utf8, 22 | qr{^Error importing @{[$error_at->short_path]}: Testing error }ms; 23 | like $dist->dist_log_file->slurp_utf8, 24 | qr{^Testing error }ms; 25 | 26 | cmp_deeply $dist->repo->releases, [ 27 | "ETHER/Acme-LookOfDisapproval-0.001.tar.gz", 28 | "ETHER/Acme-LookOfDisapproval-0.002.tar.gz", 29 | "ETHER/Acme-LookOfDisapproval-0.004.tar.gz", 30 | "ETHER/Acme-LookOfDisapproval-0.005.tar.gz", 31 | "ETHER/Acme-LookOfDisapproval-0.006.tar.gz", 32 | ], "import_releases() continues after a bad release"; 33 | }; 34 | 35 | 36 | subtest "skipping releases" => sub { 37 | my $dist = new_dist( distname => "Date-Spoken-German" ); 38 | 39 | # Insert a release to skip into the config. Be sure to clear out 40 | # the skip release cache. We pick this one because it is in a 41 | # subdirectory for extra tricks. 42 | $dist->config->skip->{releases}->push('CHRWIN/date-spoken-german/date-spoken-german-0.03.tar.gz'); 43 | $dist->config->_clear_skip_releases; 44 | 45 | $dist->import_releases( push => 0 ); 46 | 47 | cmp_deeply scalar $dist->repo->releases->sort, [sort 48 | 'CHRWIN/date-spoken-german/date-spoken-german-0.02.tar.gz', 49 | 'CHRWIN/date-spoken-german/Date-Spoken-German-0.04.tar.gz', 50 | 'CHRWIN/date-spoken-german/Date-Spoken-German-0.05.tar.gz' 51 | ]; 52 | }; 53 | 54 | 55 | subtest "Distribution with no releases" => sub { 56 | my $dist_no_releases = new_dist( distname => "ReForm" ); 57 | 58 | cmp_deeply $dist_no_releases->releases_to_import, [], 59 | "distribution has no releases"; 60 | 61 | $dist_no_releases->import_releases; 62 | ok !$dist_no_releases->repo->have_git_repo; 63 | ok !$dist_no_releases->github->exists_on_github, "did not create a Github repo"; 64 | }; 65 | 66 | 67 | subtest "Same name, different case" => sub { 68 | # Have to happen at the same time else they will delete each other 69 | my $dist1 = new_dist( distname => "ReForm" ); 70 | my $dist2 = new_dist( distname => "reform" ); 71 | 72 | $dist1->github->create_repo; 73 | $dist1->repo->wait_until_created; 74 | 75 | ok !$dist2->import_releases; 76 | 77 | like $dist2->config->gitpan_log_file->slurp_utf8, qr{^Error: distribution ReForm already exists, reform would clash\.$}ms; 78 | }; 79 | 80 | 81 | done_testing; 82 | -------------------------------------------------------------------------------- /t/Git.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Path::Tiny; 6 | 7 | use Gitpan::Test; 8 | 9 | use Gitpan::Git; 10 | 11 | note "Check the repo was created"; { 12 | my $Repo_Dir = Path::Tiny->tempdir->realpath; 13 | my $git = Gitpan::Git->init( 14 | distname => "Foo-Bar", 15 | repo_dir => $Repo_Dir 16 | ); 17 | isa_ok $git, "Gitpan::Git"; 18 | 19 | ok $git->is_empty; 20 | 21 | ok -d $Repo_Dir; 22 | ok -d $Repo_Dir->child(".git"); 23 | is $git->repo_dir, $Repo_Dir; 24 | 25 | note "Can we use an existing repo?"; 26 | my $copy = Gitpan::Git->init( 27 | distname => "Foo-Bar", 28 | repo_dir => $Repo_Dir 29 | ); 30 | isa_ok $copy, "Gitpan::Git"; 31 | is $copy->repo_dir, $Repo_Dir; 32 | } 33 | 34 | 35 | note "Test our cleanup routines"; { 36 | SKIP: { 37 | my $git = Gitpan::Git->init( 38 | distname => "Foo-Bar" 39 | ); 40 | my $hooks_dir = $git->repo_dir->child(".git", "hooks"); 41 | 42 | skip "No hooks dir" unless -d $hooks_dir; 43 | skip "No sample hooks" unless [$hooks_dir->children]->first(qr{\.sample$}); 44 | 45 | $git->clean; 46 | ok ![$hooks_dir->children]->first(qr{\.sample$}); 47 | } 48 | } 49 | 50 | 51 | note "new_or_clone"; { 52 | my $origin = Gitpan::Git->init( 53 | distname => "Foo-Bar" 54 | ); 55 | $origin->repo_dir->child("foo")->touch; 56 | $origin->add( "foo" ); 57 | $origin->commit( message => "testing clone" ); 58 | 59 | note "Repo dir does not exist."; { 60 | my $tempdir = Path::Tiny->tempdir; 61 | 62 | # This will clone. 63 | my $git = Gitpan::Git->new_or_clone( 64 | distname => "Foo-Bar", 65 | url => $origin->repo_dir.'', 66 | repo_dir => $tempdir->child("foo") 67 | ); 68 | ok -e $git->repo_dir->child("foo"); 69 | } 70 | 71 | note "Repo dir exists, but there's no repository."; { 72 | my $tempdir = Path::Tiny->tempdir; 73 | 74 | # This will clone. 75 | my $git = Gitpan::Git->new_or_clone( 76 | distname => "Foo-Bar", 77 | url => $origin->repo_dir.'', 78 | repo_dir => $tempdir 79 | ); 80 | ok -e $git->repo_dir->child("foo"); 81 | } 82 | 83 | note "Repo dir and repository exists, but the remote is wrong"; { 84 | my $tempdir = Path::Tiny->tempdir; 85 | Gitpan::Git->init( 86 | distname => "Foo-Bar", 87 | repo_dir => $tempdir 88 | ); 89 | 90 | my $git = Gitpan::Git->new_or_clone( 91 | distname => "Foo-Bar", 92 | url => $origin->repo_dir.'', 93 | repo_dir => $tempdir 94 | ); 95 | ok -e $git->repo_dir->child("foo"); 96 | } 97 | } 98 | 99 | 100 | note "Remotes"; { 101 | my $git = Gitpan::Git->init( 102 | distname => "Foo-Bar" 103 | ); 104 | 105 | is_deeply $git->remotes, {}; 106 | 107 | ok !$git->remote("foo"); 108 | 109 | $git->change_remote( foo => "http://example.com" ); 110 | 111 | is $git->remote( "foo" ), "http://example.com"; 112 | } 113 | 114 | 115 | note "Remove working copy"; { 116 | my $git = Gitpan::Git->init( 117 | distname => "Foo-Bar" 118 | ); 119 | 120 | my $file = $git->repo_dir->child("foo"); 121 | $file->touch; 122 | ok -e $file; 123 | $git->remove_working_copy; 124 | ok !-e $file; 125 | is_deeply [map { $_->basename } $git->repo_dir->children], [".git"]; 126 | } 127 | 128 | 129 | note "revision_exists"; { 130 | my $git = Gitpan::Git->init( 131 | distname => "Foo-Bar" 132 | ); 133 | 134 | $git->repo_dir->child("foo")->touch; 135 | $git->add( "foo" ); 136 | $git->commit( message => "testing" ); 137 | 138 | $git->tag("some_tag"); 139 | 140 | note "revision_exists on a branch"; 141 | my $master = $git->revision_exists("master"); 142 | isa_ok $master, "Git::Raw::Branch"; 143 | is $master->target->id, $git->head->target->id; 144 | 145 | note "revision_exists on a SHA"; 146 | my $head = $git->revision_exists($git->head->target->id); 147 | isa_ok $head, "Git::Raw::Commit"; 148 | is $head->id, $git->head->target->id; 149 | 150 | note "revision_exists on a lightweight tag"; 151 | my $tag = $git->revision_exists("some_tag"); 152 | isa_ok $tag, "Git::Raw::Reference"; 153 | is $tag->target->id, $git->head->target->id; 154 | 155 | ok !$git->revision_exists("does_not_exist"), "something which does not exist"; 156 | } 157 | 158 | 159 | note "commit & log"; { 160 | my $git = Gitpan::Git->init( 161 | distname => "Foo-Bar" 162 | ); 163 | 164 | $git->repo_dir->child("bar")->touch; 165 | $git->add( "bar" ); 166 | $git->commit( message => "testing commit author" ); 167 | 168 | ok !$git->is_empty; 169 | is $git->head->shorthand, "master"; 170 | 171 | my($last_log) = $git->log("-1"); 172 | is $last_log->committer_email, 'schwern+gitpan-test@pobox.com'; 173 | is $last_log->committer_name, 'Gitpan Test'; 174 | is $last_log->author_email, 'schwern+gitpan-test@pobox.com'; 175 | is $last_log->author_name, 'Gitpan Test'; 176 | } 177 | 178 | 179 | subtest "checkout" => sub { 180 | my $git = Gitpan::Git->init( 181 | distname => "Foo-Bar" 182 | ); 183 | 184 | note "Put a commit into master"; 185 | $git->repo_dir->child("foo")->touch; 186 | $git->add_all; 187 | $git->commit( message => "first commit in master" ); 188 | is $git->current_branch, "master"; 189 | 190 | note "Change to a different branch"; 191 | $git->run( "branch", "something" ); 192 | $git->checkout( "something" ); 193 | is $git->current_branch, "something"; 194 | 195 | note "Put a commit into something"; 196 | $git->repo_dir->child("bar")->touch; 197 | $git->add_all; 198 | $git->commit( message => "first commit in something" ); 199 | 200 | note "Switch to master"; 201 | $git->checkout( "master" ); 202 | ok -e $git->repo_dir->child("foo"); 203 | ok !-e $git->repo_dir->child("bar"); 204 | 205 | $git->repo_dir->child("in_master")->touch; 206 | $git->add_all; 207 | $git->commit( message => "make master diverge" ); 208 | 209 | note "Put something in the index"; 210 | $git->repo_dir->child("added")->touch; 211 | $git->add("added"); 212 | 213 | note "Checkout with force to clear the index"; 214 | $git->checkout( 215 | "something", 216 | "force" => 1, 217 | ); 218 | 219 | ok !-e $git->repo_dir->child("added"), "cleans added files"; 220 | ok -e $git->repo_dir->child("bar"), "checked out"; 221 | ok -e $git->repo_dir->child("foo"), "checked out"; 222 | }; 223 | 224 | 225 | note "clone, push, pull"; { 226 | my $origin = Gitpan::Git->init( 227 | distname => "Foo-Bar" 228 | ); 229 | $origin->repo_dir->child("foo")->touch; 230 | $origin->add( "foo" ); 231 | $origin->commit( message => "testing clone" ); 232 | 233 | my $clone = Gitpan::Git->clone( 234 | url => $origin->repo_dir.'', 235 | distname => "Foo-Bar" 236 | ); 237 | is $clone->current_branch, $origin->current_branch; 238 | 239 | ok -e $clone->repo_dir->child("foo"), "working directory cloned"; 240 | 241 | my($origin_log1) = $origin->log("-1"); 242 | my($clone_log1) = $clone->log("-1"); 243 | is $origin_log1->commit, $clone_log1->commit, "commit ids cloned"; 244 | 245 | # Test pull 246 | $origin->repo_dir->child("bar")->touch; 247 | $origin->add( "bar" ); 248 | $origin->commit( message => "adding bar" ); 249 | 250 | note "Pulling"; 251 | $clone->pull; 252 | 253 | ok -e $clone->repo_dir->child("bar"), "pulled new file"; 254 | 255 | my($origin_log2) = $origin->log("-1"); 256 | my($clone_log2) = $clone->log("-1"); 257 | is $origin_log2->commit, $clone_log2->commit, "commit ids pulled"; 258 | 259 | # Test push 260 | my $bare = Gitpan::Git->clone( 261 | url => $origin->repo_dir.'', 262 | distname => "Foo-Bar", 263 | options => { bare => 1 } 264 | ); 265 | my $clone2 = Gitpan::Git->clone( 266 | url => $bare->git_dir.'', 267 | distname => "Foo-Bar" 268 | ); 269 | $clone2->repo_dir->child("baz")->touch; 270 | $clone2->add( "baz" ); 271 | $clone2->commit( message => "adding baz" ); 272 | $clone2->tag( "some_tag" ); 273 | 274 | note "Pushing"; 275 | $clone2->push; 276 | my($bare_log) = $bare->log("-1"); 277 | my($clone2_log) = $clone2->log("-1"); 278 | is $bare_log->commit, $clone2_log->commit, "push"; 279 | 280 | my $tags = $bare->list_tags; 281 | is_deeply $tags, ["some_tag"], "pushing tags"; 282 | } 283 | 284 | 285 | subtest "push --ff-only" => sub { 286 | my $origin = Gitpan::Git->init( 287 | distname => "Foo-Bar" 288 | ); 289 | $origin->repo_dir->child("foo")->touch; 290 | $origin->add( "foo" ); 291 | $origin->commit( message => "testing clone" ); 292 | 293 | my $clone = Gitpan::Git->clone( 294 | url => $origin->repo_dir.'', 295 | distname => "Foo-Bar" 296 | ); 297 | 298 | # Make the clone diverge 299 | $clone->repo_dir->child("bar")->touch; 300 | $clone->add_all; 301 | $clone->commit( message => "clone is diverging" ); 302 | 303 | # And now origin 304 | $origin->repo_dir->child("baz")->touch; 305 | $origin->add_all; 306 | $origin->commit( message => "origin is diverging" ); 307 | 308 | throws_ok { 309 | $clone->pull( ff_only => 1 ); 310 | } qr/Not possible to fast-forward/; 311 | }; 312 | 313 | 314 | note "delete_repo"; { 315 | my $git = Gitpan::Git->init( 316 | distname => "Foo-Bar" 317 | ); 318 | ok -e $git->repo_dir; 319 | 320 | $git->delete_repo; 321 | ok !-e $git->repo_dir; 322 | } 323 | 324 | 325 | subtest "add_all with .gitignore" => sub { 326 | my $git = Gitpan::Git->init( 327 | distname => "Foo-Bar" 328 | ); 329 | 330 | my $gitignore = $git->repo_dir->child(".gitignore"); 331 | my $foo = $git->repo_dir->child("foo"); 332 | 333 | $gitignore->append("foo\n"); 334 | $foo->touch; 335 | 336 | cmp_deeply [map { $_->path1 } grep { $_->ignored } $git->status("--ignored")], ["foo"]; 337 | $git->add_all; 338 | cmp_deeply [grep { $_->ignored } $git->status("--ignored")], []; 339 | 340 | $git->commit( message => "first commit"); 341 | 342 | cmp_deeply [grep { $_->ignored } $git->status("--ignored")], []; 343 | }; 344 | 345 | 346 | note "rm and add all"; { 347 | my $origin = Gitpan::Git->init( 348 | distname => "Foo-Bar" 349 | ); 350 | my $clone = Gitpan::Git->clone( 351 | url => $origin->repo_dir.'', 352 | distname => "Foo-Bar" 353 | ); 354 | 355 | $origin->repo_dir->child("foo")->touch; 356 | $origin->repo_dir->child("bar")->touch; 357 | $origin->add_all; 358 | $origin->commit( message => "Adding foo and bar"); 359 | 360 | $clone->pull; 361 | ok -e $clone->repo_dir->child("foo"); 362 | ok -e $clone->repo_dir->child("bar"); 363 | 364 | $origin->rm_all; 365 | cmp_deeply [map { $_->relative($origin->repo_dir).'' } $origin->repo_dir->children], 366 | [".git"]; 367 | 368 | $origin->repo_dir->child("bar")->touch; 369 | $origin->repo_dir->child("baz")->touch; 370 | $origin->add_all; 371 | $origin->commit( message => "Adding bar and baz"); 372 | 373 | $clone->pull; 374 | ok -e $clone->repo_dir->child("bar"); 375 | ok -e $clone->repo_dir->child("baz"); 376 | } 377 | 378 | 379 | note "ref_safe_version"; { 380 | my $git = Gitpan::Git->init( 381 | distname => "Foo-Bar" 382 | ); 383 | 384 | is $git->ref_safe_version(".1"), "0.1"; 385 | } 386 | 387 | 388 | note "make_ref_safe"; { 389 | my $git = Gitpan::Git->init( 390 | distname => "Foo-Bar" 391 | ); 392 | 393 | my %refs = ( 394 | # 6. They cannot begin or end with a slash / or contain multiple consecutive 395 | # slashes. 396 | '/foo//bar/' => 'foo/bar', 397 | 398 | # 1. no slash-separated component can begin with a dot . 399 | 'foo/.bar.lock/baz' => 'foo/-bar-/baz', 400 | 401 | # 3. They cannot have two consecutive dots 402 | 'foo..bar...baz' => 'foo.bar.baz', 403 | 404 | # 8. They cannot contain a sequence @{ 405 | 'foo@{bar' => 'foo-bar', 406 | 407 | # 4. They cannot have ASCII control characters (i.e. bytes whose values 408 | # are lower than \040, or \177 DEL), space, tilde ~, caret ^, or 409 | # colon : anywhere. 410 | # 5. They cannot have question-mark ?, asterisk *, or open bracket [ anywhere 411 | # 9. They cannot be the single character @ 412 | # 10. They cannot contain a \ 413 | qq{1\a2\x{7f}3\a4 5\n6\t7~8^9:10?11*12[13\@14\\15} 414 | => '1-2-3-4-5-6-7-8-9-10-11-12-13-14-15', 415 | 416 | # 7. They cannot end with a dot 417 | 'foo/bar.' => 'foo/bar-' 418 | ); 419 | 420 | %refs->each(func($have, $want) { 421 | is $git->make_ref_safe($have), $want or diag "make_ref_safe($have)"; 422 | }); 423 | } 424 | 425 | 426 | note "maturity2tag"; { 427 | my $git = Gitpan::Git->init( 428 | distname => "Foo-Bar" 429 | ); 430 | 431 | is $git->maturity2tag("released"), "stable"; 432 | is $git->maturity2tag("developer"), "alpha"; 433 | is $git->maturity2tag("blah"), "blah"; 434 | } 435 | 436 | note "Commit release"; { 437 | my $git = Gitpan::Git->init( 438 | distname => "Foo-Bar" 439 | ); 440 | 441 | use Gitpan::Release; 442 | my $release = Gitpan::Release->new( 443 | distname => 'Acme-LookOfDisapproval', 444 | version => '0.005', 445 | ); 446 | 447 | $git->repo_dir->child("foo")->touch; 448 | $git->repo_dir->child("bar")->touch; 449 | $git->add_all; 450 | $git->commit_release($release); 451 | 452 | my($last_commit) = $git->log("-1"); 453 | is $last_commit->author_name, "Karen Etheridge"; 454 | is $last_commit->author_email, 'ether@cpan.org'; 455 | is $last_commit->committer_name, 'Gitpan Test'; 456 | is $last_commit->committer_email, 'schwern+gitpan-test@pobox.com'; 457 | is $last_commit->author_gmtime, 1382763843; 458 | 459 | my $log_message = $last_commit->message; 460 | like $log_message, qr{^Import of ETHER/Acme-LookOfDisapproval-0.005.tar.gz from CPAN}ms; 461 | like $log_message, qr{^gitpan-cpan-distribution:\s+Acme-LookOfDisapproval}ms; 462 | like $log_message, qr{^gitpan-cpan-version:\s+0.005}ms; 463 | like $log_message, qr{^gitpan-cpan-path:\s+ETHER/Acme-LookOfDisapproval-0.005.tar.gz}ms; 464 | like $log_message, qr{^gitpan-cpan-author:\s+ETHER}ms; 465 | like $log_message, qr{^gitpan-cpan-maturity:\s+released}ms; 466 | 467 | cmp_deeply $git->cpan_paths, ["ETHER/Acme-LookOfDisapproval-0.005.tar.gz"]; 468 | cmp_deeply $git->gitpan_versions, ["0.005"]; 469 | cmp_deeply $git->cpan_versions, ["0.005"]; 470 | ok $git->get_tag("stable"); 471 | ok $git->get_tag("ETHER"); 472 | } 473 | 474 | 475 | note "Commit release, no author name"; { 476 | my $git = Gitpan::Git->init( 477 | distname => "Acme-Maybe" 478 | ); 479 | 480 | use Gitpan::Release; 481 | my $release = Gitpan::Release->new( 482 | distname => 'Acme-Maybe', 483 | version => '0.01', 484 | ); 485 | 486 | $git->repo_dir->child("foo")->touch; 487 | $git->repo_dir->child("bar")->touch; 488 | $git->add_all; 489 | $git->commit_release($release); 490 | 491 | my($last_commit) = $git->log("-1"); 492 | is $last_commit->author_name, "MAXA"; 493 | is $last_commit->author_email, 'maxa@cpan.org'; 494 | } 495 | 496 | 497 | # Releases with no version would make a tag called "cpan_version" 498 | # instead of cpan_version/$version and this would make the next 499 | # release unable to tag. 500 | note "tag_release, no version"; { 501 | my $git = Gitpan::Git->init( 502 | distname => "Acme-Blarghy-McBlarghBlargh" 503 | ); 504 | 505 | use Gitpan::Release; 506 | my $release0 = Gitpan::Release->new( 507 | distname => 'Acme-Blarghy-McBlarghBlargh', 508 | version => '', 509 | ); 510 | 511 | $git->repo_dir->child("foo")->touch; 512 | $git->repo_dir->child("bar")->touch; 513 | $git->add_all; 514 | $git->commit_release($release0); 515 | 516 | my $release2 = Gitpan::Release->new( 517 | distname => 'Acme-Blarghy-McBlarghBlargh', 518 | version => '0.002', 519 | ); 520 | $git->repo_dir->child("foo")->spew("stuff\n"); 521 | $git->add_all; 522 | $git->commit_release($release2); 523 | 524 | cmp_deeply scalar $git->cpan_paths->sort, 525 | [sort 526 | "DHOSS/Acme-Blarghy-McBlarghBlargh.tar.gz", 527 | "DHOSS/Acme-Blarghy-McBlarghBlargh-0.002.tar.gz" 528 | ]; 529 | cmp_deeply $git->gitpan_versions, ['0.002']; 530 | cmp_deeply $git->cpan_versions, ['0.002']; 531 | ok $git->get_tag("stable"); 532 | ok $git->get_tag("DHOSS"); 533 | } 534 | 535 | 536 | done_testing; 537 | -------------------------------------------------------------------------------- /t/Git/prepare_for_commits.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Git; 8 | 9 | subtest "prepare_for_commits on an empty repository" => sub { 10 | my $git = Gitpan::Git->init( 11 | distname => "Foo-Bar" 12 | ); 13 | 14 | lives_ok { $git->prepare_for_commits; } 15 | "Doesn't choke when there's no files"; 16 | 17 | # Untracked file 18 | $git->repo_dir->child("foo")->touch; 19 | 20 | # Staged file 21 | $git->repo_dir->child("bar")->touch; 22 | $git->add( "bar"); 23 | 24 | $git->prepare_for_commits; 25 | pass("works on a repo with no commits"); 26 | 27 | ok !-e $git->repo_dir->child("foo"), "deletes uncommitted files"; 28 | ok !-e $git->repo_dir->child("bar"), "restores unstaged state"; 29 | 30 | cmp_deeply [$git->status], [], "staging is empty"; 31 | }; 32 | 33 | 34 | subtest "prepare_for_commits on a non-empty repository" => sub { 35 | my $git = Gitpan::Git->init( 36 | distname => "Foo-Bar" 37 | ); 38 | 39 | my $foo = $git->repo_dir->child("foo"); 40 | my $bar = $git->repo_dir->child("bar"); 41 | 42 | # Make the repository non-empty 43 | $foo->touch; 44 | $bar->spew("first commit"); 45 | $git->add_all; 46 | $git->commit( message => "first commit" ); 47 | 48 | is $git->current_branch, "master"; 49 | 50 | # Change to a different branch 51 | $git->run( "branch", "something" ); 52 | $git->checkout( "something" ); 53 | 54 | is $git->current_branch, "something"; 55 | 56 | # Unstaged change 57 | $foo->append("Something\nSomething\n"); 58 | 59 | # Staged change 60 | $bar->append("Something else"); 61 | $git->add("bar"); 62 | 63 | # Untracked file 64 | $git->repo_dir->child("untracked")->touch; 65 | 66 | $git->prepare_for_commits; 67 | 68 | is $git->current_branch, "master", "branch set to master"; 69 | ok !-e $git->repo_dir->child("untracked"), "untracked files deleted"; 70 | 71 | ok -e $foo; 72 | ok -e $bar; 73 | is $foo->slurp, ""; 74 | is $bar->slurp, "first commit"; 75 | 76 | cmp_deeply [$git->status], [], "staging is empty"; 77 | }; 78 | 79 | done_testing; 80 | -------------------------------------------------------------------------------- /t/Github.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | 6 | use Gitpan::Test; 7 | 8 | use Gitpan::Github; 9 | 10 | note "repo_name_on_github()"; { 11 | my %tests = ( 12 | "gitpan" => "gitpan", 13 | "Foo-Bar" => "Foo-Bar", 14 | "Some_Thing" => "Some_Thing", 15 | "This::Thât" => "This-Th-t", 16 | "Testing-ünicode" => "Testing--nicode", 17 | "bpd.PW44" => "bpd.PW44", 18 | "perl-5.005_02+apache1.3.3+modperl" => "perl-5.005_02-apache1.3.3-modperl", 19 | "Acme-Working-Out-Dependencies-From-META-yml-Or-META-json-Will-Be-Wrong-At-Some-Point-Like-This-Module-For-Instance" => "Acme-Working-Out-Dependencies-From-META-yml-Or-META-json-Will-Be-Wrong-At-Some-Point-Like-This-Modul" 20 | ); 21 | 22 | %tests->each(func($have, $want) { 23 | my $gh = Gitpan::Github->new( repo => $have ); 24 | is $gh->repo_name_on_github, $want, "$have -> $want"; 25 | }); 26 | } 27 | 28 | 29 | note "get_repo_info()"; { 30 | my $gh = Gitpan::Github->new( repo => "super-python", owner => "evalEmpire" ); 31 | ok !$gh->get_repo_info; 32 | 33 | $gh = Gitpan::Github->new( owner => "evalEmpire", repo => "gitpan" ); 34 | is $gh->get_repo_info->{name}, 'gitpan'; 35 | 36 | $gh = Gitpan::Github->new( owner => "evalEmpire", repo => "GITPAN" ); 37 | is $gh->get_repo_info->{name}, 'gitpan'; 38 | } 39 | 40 | 41 | note "exists_on_github()"; { 42 | my $gh = Gitpan::Github->new( owner => "evalEmpire", repo => "gitpan" ); 43 | ok $gh->exists_on_github; 44 | 45 | $gh = Gitpan::Github->new( owner => "evalEmpire", repo => "super-python" ); 46 | ok !$gh->exists_on_github; 47 | 48 | $gh = Gitpan::Github->new( repo => "i-do-not-exist-pretty-sure" ); 49 | ok !$gh->exists_on_github; 50 | } 51 | 52 | 53 | note "remote"; { 54 | my $gh = Gitpan::Github->new( repo => "gitpan" ); 55 | 56 | like $gh->remote(), qr{^https://.*?:\@github.com/gitpan-test/gitpan.git}; 57 | } 58 | 59 | 60 | note "create and delete repos"; { 61 | my $gh = Gitpan::Github->new( 62 | repo => rand_distname()."-Ünicode", 63 | ); 64 | 65 | ok !$gh->exists_on_github; 66 | lives_ok { $gh->delete_repo_if_exists; }; 67 | 68 | $gh->create_repo( 69 | desc => "Testing Ünicode", 70 | homepage => "http://example.com/Ünicode" 71 | ); 72 | ok $gh->_exists_on_github_cache, "create sets the exists cache"; 73 | ok $gh->exists_on_github; 74 | 75 | $gh->delete_repo_if_exists; 76 | 77 | ok !$gh->_exists_on_github_cache, "delete unsets the exists cache"; 78 | ok !$gh->exists_on_github; 79 | } 80 | 81 | 82 | subtest "change_repo_info" => sub { 83 | my $gh = Gitpan::Github->new( 84 | repo => rand_distname() 85 | ); 86 | $gh->create_repo; 87 | 88 | $gh->change_repo_info( 89 | description => "For testing", 90 | retry_if_not_found => 1 91 | ); 92 | my $info = $gh->get_repo_info; 93 | is $info->{description}, "For testing"; 94 | }; 95 | 96 | 97 | subtest "branch_info" => sub { 98 | my $gh = Gitpan::Github->new( 99 | repo => rand_distname() 100 | ); 101 | 102 | throws_ok { 103 | $gh->is_empty( retry_if_not_found => 1 ); 104 | } qr/404 Not Found/i; 105 | 106 | $gh->create_repo; 107 | 108 | ok $gh->is_empty( retry_if_not_found => 1 ), "is_empty"; 109 | 110 | require Gitpan::Git; 111 | my $git = Gitpan::Git->clone( 112 | url => $gh->remote, 113 | distname => $gh->distname 114 | ); 115 | note $git->dist_log_file; 116 | 117 | $git->repo_dir->child("foo")->touch; 118 | $git->add_all; 119 | $git->commit( message => "for testing" ); 120 | 121 | # Push at the same time we're trying to get branch info 122 | my $child = child { 123 | note "Trying push"; 124 | $git->push; 125 | note "push done"; 126 | }; 127 | my $info = $gh->branch_info( retry_if_not_found => 1 ); 128 | $child->wait; 129 | 130 | ok !$gh->is_empty, "!is_empty"; 131 | is $info->{commit}{sha}, $git->head->target->id, "git and Github match after push"; 132 | }; 133 | 134 | 135 | done_testing(); 136 | -------------------------------------------------------------------------------- /t/Gitpan.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::Test; 5 | use Gitpan::perl5i; 6 | 7 | my $CLASS = 'Gitpan'; 8 | 9 | require_ok $CLASS; 10 | 11 | subtest "timestamps" => sub { 12 | my $gitpan = new_ok $CLASS; 13 | 14 | my $date = $gitpan->latest_release_date; 15 | # This will break in about three years, sorry future. 16 | like $date, qr{\A 14\d{8} \z}x; 17 | 18 | is $gitpan->read_latest_release_timestamp, 0; 19 | 20 | $gitpan->write_latest_release_timestamp( $date ); 21 | is $gitpan->read_latest_release_timestamp, $date; 22 | }; 23 | 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/HasBackpanIndex.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | { 8 | package Some::Class; 9 | use Gitpan::OO; 10 | with 'Gitpan::Role::HasBackpanIndex'; 11 | } 12 | 13 | note "shared index"; { 14 | my $obj1 = Some::Class->new; 15 | isa_ok $obj1->backpan_index, "BackPAN::Index"; 16 | 17 | my $obj2 = Some::Class->new; 18 | isa_ok $obj2->backpan_index, "BackPAN::Index"; 19 | 20 | is $obj1->backpan_index->mo->id, $obj2->backpan_index->mo->id, "objects share index objects"; 21 | } 22 | 23 | note "not shared across forks"; { 24 | my $obj1 = Some::Class->new; 25 | isa_ok $obj1->backpan_index, "BackPAN::Index"; 26 | 27 | my $child = child { 28 | my $self = shift; 29 | 30 | my $obj2 = Some::Class->new; 31 | my $obj3 = Some::Class->new; 32 | 33 | my @ok; 34 | push @ok, isnt $obj1->backpan_index->mo->id, 35 | $obj2->backpan_index->mo->id, "forking gets a new index"; 36 | push @ok, is $obj2->backpan_index->mo->id, 37 | $obj3->backpan_index->mo->id, "still shared in the same process"; 38 | $self->say(scalar @ok); 39 | } pipe => 1; 40 | 41 | $child->wait; 42 | Test::More->builder->current_test( Test::More->builder->current_test + $child->read ); 43 | } 44 | 45 | note "test config"; { 46 | my $obj = Some::Class->new; 47 | my $bp = $obj->backpan_index; 48 | 49 | ok("t"->path->subsumes($bp->cache_dir->path), 50 | "backpan cache is in the t/ directory") 51 | or diag $bp->cache_dir; 52 | 53 | is $bp->backpan_index_url->scheme, "file"; 54 | is $bp->backpan_index_url->path, "t/backpan/backpan-index.gz"; 55 | } 56 | 57 | done_testing; 58 | -------------------------------------------------------------------------------- /t/HasCPANAuthors.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | note "Setup testing class"; { 8 | package Foo; 9 | use Moo; 10 | with "Gitpan::Role::HasCPANAuthors"; 11 | } 12 | 13 | note "Authors"; { 14 | my $obj = new_ok "Foo"; 15 | my $authors = $obj->cpan_authors; 16 | isa_ok $authors, "Gitpan::CPAN::Authors"; 17 | 18 | my $author = $authors->author("MSCHWERN"); 19 | is $author->cpanid, "MSCHWERN"; 20 | is $author->name, 'Michael G Schwern'; 21 | is $author->email, 'mschwern@cpan.org'; 22 | is $author->url, 'http://schwern.net'; 23 | } 24 | 25 | note "Same object"; { 26 | my $obj1 = new_ok "Foo"; 27 | my $obj2 = new_ok "Foo"; 28 | 29 | is $obj1->cpan_authors->mo->id, $obj2->cpan_authors->mo->id; 30 | } 31 | 32 | note "unicode names"; { 33 | my $obj = new_ok "Foo"; 34 | 35 | my $author = $obj->cpan_authors->author("AVAR"); 36 | is $author->name, "Ævar Arnfjörð Bjarmason"; 37 | } 38 | 39 | note "CENSORED email fallback"; { 40 | my $obj = new_ok "Foo"; 41 | 42 | my $author = $obj->cpan_authors->author("AANOAA"); 43 | is $author->email, 'aanoaa@cpan.org'; 44 | } 45 | 46 | note "Wacky email fallback"; { 47 | my $obj = new_ok "Foo"; 48 | 49 | my $authors = $obj->cpan_authors; 50 | 51 | # SPROUT has an invalid email 52 | is $authors->author("SPROUT")->email, 'sprout@cpan.org'; 53 | 54 | # MSISK has a valid one which is not a cpan.org address 55 | is $authors->author("MSISK")->email, 'sisk@mojotoad.com'; 56 | 57 | # MYSOCIETY has angle brackets which can get past Email::Valid 58 | is $authors->author("MYSOCIETY")->email, 'mysociety@cpan.org'; 59 | } 60 | 61 | done_testing; 62 | -------------------------------------------------------------------------------- /t/HasConfig.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | note "Setup test classes"; { 8 | package Some::Class1; 9 | use Gitpan::OO; 10 | with 'Gitpan::Role::HasConfig'; 11 | 12 | package Some::Class2; 13 | use Gitpan::OO; 14 | with 'Gitpan::Role::HasConfig'; 15 | } 16 | 17 | note "Config is shared"; { 18 | my $obj1 = Some::Class1->new; 19 | isa_ok $obj1->config, "Gitpan::Config"; 20 | 21 | my $obj2 = Some::Class2->new; 22 | isa_ok $obj2->config, "Gitpan::Config"; 23 | 24 | is $obj1->config->mo->id, $obj2->config->mo->id; 25 | } 26 | 27 | note "As class method"; { 28 | isa_ok( Some::Class1->config, "Gitpan::Config" ); 29 | } 30 | 31 | subtest "config changes with Gitpan::Config->default" => sub { 32 | my $obj = Some::Class1->new; 33 | 34 | my $config = $obj->config; 35 | 36 | my $new_config = Gitpan::Config->new; 37 | Gitpan::Config->set_default($new_config); 38 | 39 | is $obj->config->mo->id, $new_config->mo->id; 40 | }; 41 | 42 | done_testing; 43 | -------------------------------------------------------------------------------- /t/HasUA.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | { 8 | package Some::Class; 9 | use Gitpan::OO; 10 | with 'Gitpan::Role::HasUA'; 11 | } 12 | 13 | note "shared ua"; { 14 | my $obj1 = Some::Class->new; 15 | isa_ok $obj1->ua, "LWP::UserAgent"; 16 | 17 | my $obj2 = Some::Class->new; 18 | isa_ok $obj2->ua, "LWP::UserAgent"; 19 | 20 | is $obj1->ua->mo->id, $obj2->ua->mo->id, "objects share ua objects"; 21 | } 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /t/OO.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | note "haz"; { 8 | { 9 | package Foo; 10 | use Gitpan::OO; 11 | use Gitpan::Types; 12 | 13 | has "stuff" => 14 | is => 'rw'; 15 | 16 | haz "thing"; 17 | 18 | haz "file" => 19 | isa => Path; 20 | } 21 | 22 | my $obj = new_ok "Foo"; 23 | 24 | $obj->stuff(23); 25 | is $obj->stuff, 23, "has works"; 26 | 27 | $obj->thing(42); 28 | is $obj->thing, 42, "haz defaults to rw"; 29 | 30 | $obj->file("/foo/bar/baz"); 31 | is $obj->file, "/foo/bar/baz"; 32 | isa_ok $obj->file, "Path::Tiny", "coercion is automatic"; 33 | } 34 | 35 | done_testing; 36 | -------------------------------------------------------------------------------- /t/Release.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | 6 | use Gitpan::Test; 7 | use Gitpan::Release; 8 | 9 | note "Required args"; { 10 | dies_ok { Gitpan::Release->new; }; 11 | dies_ok { 12 | Gitpan::Release->new( distname => "Acme-Pony" ); 13 | }; 14 | dies_ok { 15 | Gitpan::Release->new( version => '1.1.1' ); 16 | }; 17 | } 18 | 19 | 20 | note "The basics"; { 21 | my $pony = Gitpan::Release->new( 22 | distname => 'Acme-Pony', 23 | version => '1.1.1' 24 | ); 25 | isa_ok $pony, "Gitpan::Release"; 26 | 27 | is $pony->path, "authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.1.tar.gz"; 28 | is $pony->short_path, "DCANTRELL/Acme-Pony-1.1.1.tar.gz"; 29 | } 30 | 31 | 32 | note "short_path"; { 33 | my $german = new_ok 'Gitpan::Release', [ 34 | distname => 'Date-Spoken-German', 35 | version => '0.04' 36 | ]; 37 | 38 | is $german->path, 'authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.04.tar.gz'; 39 | is $german->short_path, 'CHRWIN/date-spoken-german/Date-Spoken-German-0.04.tar.gz'; 40 | } 41 | 42 | 43 | note "From a backpan release"; { 44 | my $pony = Gitpan::Release->new( 45 | distname => 'Acme-Pony', 46 | version => '1.1.1', 47 | ); 48 | 49 | my $pony2 = Gitpan::Release->new( 50 | backpan_release => $pony->backpan_release 51 | ); 52 | isa_ok $pony2, "Gitpan::Release"; 53 | is $pony2->distname, "Acme-Pony"; 54 | is $pony2->version, "1.1.1"; 55 | } 56 | 57 | 58 | note "Author info"; { 59 | my $pony = Gitpan::Release->new( 60 | distname => 'Acme-Pony', 61 | version => '1.1.1' 62 | ); 63 | isa_ok $pony, "Gitpan::Release"; 64 | 65 | my $author = $pony->author; 66 | is $author->cpanid, "DCANTRELL"; 67 | like $author->name, qr{Cantrell}i; 68 | } 69 | 70 | 71 | subtest "gitpan_version" => sub { 72 | my $release = Gitpan::Release->new( 73 | distname => 'Acme-eng2kor', 74 | version => '0.0.1' 75 | ); 76 | 77 | is $release->version, '0.0.1'; 78 | is $release->gitpan_version, '0.0.1'; 79 | 80 | $release = Gitpan::Release->new( 81 | distname => 'Acme-eng2kor', 82 | version => 'v0.0.2' 83 | ); 84 | 85 | is $release->version, 'v0.0.2'; 86 | is $release->gitpan_version, '0.0.2'; 87 | }; 88 | 89 | 90 | note "get"; { 91 | my $pony = new_ok "Gitpan::Release", [ 92 | distname => 'Acme-Pony', 93 | version => '1.1.1' 94 | ]; 95 | 96 | my $file = $pony->archive_file; 97 | like $file, qr{Acme-Pony}; 98 | ok !-e $file; 99 | 100 | my $res = $pony->get; 101 | ok $res->is_success; 102 | ok -e $pony->archive_file; 103 | } 104 | 105 | 106 | subtest "get - bad size" => sub { 107 | my $release = new_ok "Gitpan::Release", [ 108 | distname => 'Lingua-JA-WordNet', 109 | version => '0.21' 110 | ]; 111 | 112 | throws_ok { 113 | $release->get(); 114 | } qr/File not fully retrieved, got 139495, expected 60831235/; 115 | 116 | ok $release->get( check_size => 0 ); 117 | }; 118 | 119 | 120 | subtest "get from file urls" => sub { 121 | my $pony = new_ok "Gitpan::Release", [ 122 | distname => 'Acme-Pony', 123 | version => '1.1.1' 124 | ]; 125 | 126 | my $res = $pony->get; 127 | ok $res->is_success; 128 | is $pony->archive_file, ($pony->url->path.'')->path->absolute; 129 | }; 130 | 131 | 132 | subtest "get - file url does not exist" => sub { 133 | my $release = new_ok "Gitpan::Release", [ 134 | distname => 'Acme-Pony', 135 | version => '1.1.1', 136 | url => 'file:///blah/blabity/blah' 137 | ]; 138 | 139 | throws_ok { 140 | $release->get; 141 | } qr{^Could not find /blah/blabity/blah}; 142 | }; 143 | 144 | note "move"; { 145 | my $to = Path::Tiny->tempdir; 146 | 147 | my $pony = new_ok "Gitpan::Release", [ 148 | distname => 'Acme-Warn-LOLCAT', 149 | version => '0.01' 150 | ]; 151 | 152 | $pony->get; 153 | $pony->move($to); 154 | ok !$pony->extract_dir, "Releases are not extracted after moving"; 155 | 156 | ok ! -e $to->child(".git"); 157 | 158 | $pony->extract; 159 | cmp_deeply [sort map { $_->basename } $to->children], 160 | [sort grep !/^.git$/, map { $_->basename } $pony->extract_dir->children]; 161 | } 162 | 163 | done_testing; 164 | -------------------------------------------------------------------------------- /t/Release/extract.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | 6 | use Gitpan::Test; 7 | use Gitpan::Release; 8 | 9 | 10 | subtest "basic extract" => sub { 11 | my $pony = new_ok "Gitpan::Release", [ 12 | distname => 'Acme-Pony', 13 | version => '1.1.1' 14 | ]; 15 | 16 | throws_ok { 17 | $pony->extract; 18 | } qr{\Q@{[$pony->archive_file]} does not exist}; 19 | 20 | $pony->get; 21 | my $path = $pony->extract; 22 | is $path, $pony->extract_dir; 23 | ok -d $path; 24 | ok -e $path->child("Makefile.PL"); 25 | }; 26 | 27 | 28 | subtest "empty archive" => sub { 29 | my $empty = new_ok "Gitpan::Release", [ 30 | distname => 'Bundle-Slash', 31 | version => '2.11' 32 | ]; 33 | 34 | $empty->get; 35 | 36 | throws_ok { 37 | $empty->extract; 38 | } qr{^Archive is empty}; 39 | 40 | ok !$empty->extract_dir; 41 | }; 42 | 43 | 44 | subtest "bad links" => sub { 45 | my $bad_links = new_ok "Gitpan::Release", [ 46 | distname => 'SMTP-Server', 47 | version => '1.1' 48 | ]; 49 | 50 | $bad_links->get; 51 | $bad_links->extract; 52 | ok $bad_links->extract_dir; 53 | 54 | my $tmp = Path::Tiny->tempdir; 55 | $bad_links->move($tmp); 56 | 57 | ok -l $tmp->child("Server/.#Spam.pm"); 58 | }; 59 | 60 | 61 | subtest "Too big for Github" => sub { 62 | my $too_big = new_ok "Gitpan::Release", [ 63 | distname => 'Lingua-JA-WordNet', 64 | version => '0.21' 65 | ]; 66 | 67 | $too_big->get( check_size => 0 ); 68 | $too_big->extract; 69 | 70 | my $small_file = $too_big->extract_dir->child("share/LICENSE.txt"); 71 | cmp_ok -s $small_file, ">", 1800, "small files preserved"; 72 | 73 | my $big_file = $too_big->extract_dir->child("share/wnjpn-1.1_and_synonyms-1.0.db"); 74 | 75 | my $big_file_size_in_megs = 101; 76 | my $too_big_url = "http://backpan.cpan.org/authors/id/P/PA/PAWAPAWA/Lingua-JA-WordNet-0.21.tar.gz"; 77 | 78 | is $big_file->slurp_utf8, <<"END", "big files truncated"; 79 | Sorry, this file has been truncated by Gitpan. 80 | It was $big_file_size_in_megs megs which exceeds Github's limit of 100 megs per file. 81 | You can get the file from the original archive at $too_big_url 82 | END 83 | 84 | }; 85 | 86 | 87 | subtest "tempdir cleanup" => sub { 88 | my $pony_work_dir; 89 | my $pony_archive_file; 90 | { 91 | my $pony = new_ok "Gitpan::Release", [ 92 | distname => 'Acme-Pony', 93 | version => '1.1.1' 94 | ]; 95 | 96 | $pony->get( get_file_urls => 1 ); 97 | $pony->extract; 98 | $pony_work_dir = $pony->work_dir.''; 99 | $pony_archive_file = $pony->archive_file.''; 100 | } 101 | 102 | note "Make sure we didn't copy the temp objects."; 103 | ok !ref $pony_work_dir; 104 | ok !ref $pony_archive_file; 105 | 106 | note "Temp files cleaned up in object destruction"; 107 | ok !-e $pony_work_dir; 108 | ok !-e $pony_archive_file; 109 | }; 110 | 111 | 112 | subtest "archives which extract to cwd" => sub { 113 | my $release = new_ok "Gitpan::Release", [ 114 | distname => 'RandomJungle', 115 | version => '0.05' 116 | ]; 117 | 118 | $release->get; 119 | $release->extract; 120 | 121 | ok -e $release->extract_dir->child("META.json"); 122 | }; 123 | 124 | done_testing; 125 | -------------------------------------------------------------------------------- /t/Repo.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | my $CLASS = 'Gitpan::Repo'; 8 | 9 | require_ok $CLASS; 10 | 11 | subtest "basics" => sub { 12 | my $repo = new_repo; 13 | 14 | ok !$repo->has_github, "github is created lazily"; 15 | ok !$repo->has_git, "git is created lazily"; 16 | }; 17 | 18 | 19 | note "github"; { 20 | my $repo = new_repo; 21 | 22 | my $gh = $repo->github; 23 | isa_ok $gh, "Gitpan::Github"; 24 | is $gh->owner, "gitpan-test"; 25 | is $gh->repo, $repo->distname; 26 | } 27 | 28 | 29 | note "git"; { 30 | my $repo = new_repo; 31 | 32 | my $git = $repo->git; 33 | isa_ok $git, "Gitpan::Git"; 34 | 35 | ok -d $repo->repo_dir; 36 | ok -d $repo->repo_dir->child(".git"); 37 | 38 | my $name_path = $repo->distname_path; 39 | like $repo->repo_dir, qr{\Q$name_path}, "repo_dir contains the dist name"; 40 | } 41 | 42 | 43 | note "delete_repo that doesn't exist"; { 44 | my $repo = new_repo; 45 | 46 | ok !$repo->github->exists_on_github; 47 | ok !-e $repo->repo_dir; 48 | 49 | $repo->delete_repo; 50 | 51 | ok !-e $repo->repo_dir; 52 | ok !$repo->github->exists_on_github; 53 | } 54 | 55 | 56 | note "delete_repo"; { 57 | my $repo = new_repo; 58 | 59 | $repo->github->maybe_create; 60 | my $git = $repo->git; 61 | 62 | ok -e $repo->repo_dir; 63 | ok $repo->github->exists_on_github; 64 | 65 | $repo->delete_repo; 66 | 67 | ok !-e $repo->repo_dir; 68 | ok !$repo->github->exists_on_github; 69 | ok !$repo->has_git; 70 | } 71 | 72 | 73 | subtest "empty repo" => sub { 74 | my $repo = new_repo; 75 | ok !$repo->have_git_repo; 76 | ok !$repo->have_github_repo; 77 | 78 | cmp_deeply $repo->releases, []; 79 | ok !$repo->have_git_repo, "releases() does not create a git repo"; 80 | }; 81 | 82 | 83 | subtest "releases() syncs from Github" => sub { 84 | my $dist = new_dist( distname => "Acme-Pony" ); 85 | my $repo = $dist->repo; 86 | 87 | $repo->import_releases( 88 | releases => $dist->releases_to_import 89 | ); 90 | cmp_bag $repo->releases, [ 91 | "DCANTRELL/Acme-Pony-1.1.1.tar.gz", 92 | "DCANTRELL/Acme-Pony-1.1.2.tar.gz" 93 | ], "releases() after import with git repo"; 94 | 95 | # Delete the Git repo 96 | $repo->git->delete_repo; 97 | 98 | # Use a fresh object to simulate a later run with a Github repo 99 | # but no Git. 100 | my $repo2 = new_repo( 101 | distname => "Acme-Pony", 102 | overwrite => 0 103 | ); 104 | cmp_bag $repo2->releases, [ 105 | "DCANTRELL/Acme-Pony-1.1.1.tar.gz", 106 | "DCANTRELL/Acme-Pony-1.1.2.tar.gz" 107 | ], "releases() with Github but no git repo"; 108 | }; 109 | 110 | done_testing; 111 | -------------------------------------------------------------------------------- /t/Repo/prepare_for_commits.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Git; 8 | use Gitpan::Github; 9 | 10 | my $CLASS = 'Gitpan::Repo'; 11 | 12 | require_ok $CLASS; 13 | 14 | subtest "prepare_for_commits, no git, no github" => sub { 15 | my $repo = new_repo; 16 | 17 | ok $repo->prepare_for_commits; 18 | ok $repo->is_prepared_for_commits; 19 | ok !$repo->is_prepared_for_push; 20 | ok $repo->git->is_empty; 21 | ok !$repo->github->exists_on_github; 22 | }; 23 | 24 | 25 | subtest "prepare_for_commits, git repo, no Github" => sub { 26 | my $repo = new_repo; 27 | 28 | # Make a git repo. 29 | my $git = Gitpan::Git->init( 30 | repo_dir => $repo->repo_dir, 31 | distname => $repo->distname 32 | ); 33 | 34 | # Put a commit in it to make sure we don't blow the repo away 35 | $git->repo_dir->child("foo")->touch; 36 | $git->add( "foo" ); 37 | $git->commit( message => "testing" ); 38 | 39 | ok $repo->prepare_for_commits; 40 | ok $repo->is_prepared_for_commits; 41 | ok !$repo->is_prepared_for_push; 42 | ok !$repo->git->is_empty; 43 | ok -e $repo->repo_dir->child("foo"); 44 | ok !$repo->github->exists_on_github; 45 | }; 46 | 47 | 48 | done_testing; 49 | -------------------------------------------------------------------------------- /t/Repo/prepare_for_push.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Git; 8 | use Gitpan::Github; 9 | 10 | my $CLASS = 'Gitpan::Repo'; 11 | 12 | require_ok $CLASS; 13 | 14 | subtest "prepare_for_push, no git, no github" => sub { 15 | my $repo = new_repo; 16 | 17 | ok $repo->prepare_for_push; 18 | ok $repo->is_prepared_for_commits; 19 | ok $repo->is_prepared_for_push; 20 | ok $repo->git->is_empty; 21 | ok $repo->github->exists_on_github; 22 | 23 | # Try to push a commit 24 | $repo->git->repo_dir->child("foo"); 25 | $repo->git->add_all; 26 | $repo->git->commit( message => "testing commit" ); 27 | 28 | lives_ok { $repo->git->push; }; 29 | ok $repo->are_git_and_github_on_the_same_commit; 30 | }; 31 | 32 | 33 | subtest "prepare_for_push, git repo, no Github" => sub { 34 | my $repo = new_repo; 35 | 36 | # Make a git repo. 37 | my $git = Gitpan::Git->init( 38 | repo_dir => $repo->repo_dir, 39 | distname => $repo->distname 40 | ); 41 | 42 | # Put a commit in it to make sure we don't blow the repo away 43 | $git->repo_dir->child("foo")->touch; 44 | $git->add( "foo" ); 45 | $git->commit( message => "testing" ); 46 | 47 | ok $repo->prepare_for_push; 48 | ok $repo->is_prepared_for_commits; 49 | ok $repo->is_prepared_for_push; 50 | ok !$repo->git->is_empty; 51 | ok -e $repo->repo_dir->child("foo"); 52 | ok $repo->github->exists_on_github; 53 | 54 | lives_ok { $repo->git->push; }; 55 | ok $repo->are_git_and_github_on_the_same_commit; 56 | }; 57 | 58 | 59 | done_testing; 60 | -------------------------------------------------------------------------------- /t/Repo/sync_with_github.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | my $CLASS = 'Gitpan::Repo'; 8 | 9 | require_ok $CLASS; 10 | 11 | subtest "No github, no git" => sub { 12 | my $repo = new_repo; 13 | 14 | $repo->sync_with_github; 15 | 16 | ok $repo->is_synced_with_github; 17 | ok !$repo->is_prepared_for_commits; 18 | ok !$repo->is_prepared_for_push; 19 | 20 | ok !$repo->have_git_repo; 21 | ok !$repo->have_github_repo; 22 | }; 23 | 24 | 25 | subtest "Git, no Github" => sub { 26 | my $repo = new_repo; 27 | 28 | my $git = $repo->git; 29 | 30 | $repo->sync_with_github; 31 | 32 | ok $repo->is_synced_with_github; 33 | ok !$repo->is_prepared_for_commits; 34 | ok !$repo->is_prepared_for_push; 35 | 36 | ok $repo->have_git_repo; 37 | ok !$repo->have_github_repo; 38 | }; 39 | 40 | 41 | subtest "No Git, have Github" => sub { 42 | my $repo = new_repo; 43 | 44 | # Make a Github repo. 45 | my $github = $repo->github; 46 | $github->create_repo; 47 | 48 | # Put some commits into the Github repo 49 | # But don't put the git repo where Gitpan::Repo will find it 50 | my $git = Gitpan::Git->clone( 51 | url => $github->remote, 52 | distname => $repo->distname 53 | ); 54 | $git->repo_dir->child("foo")->touch; 55 | $git->add( "foo" ); 56 | $git->commit( message => "testing" ); 57 | $git->push; 58 | 59 | ok $repo->sync_with_github; 60 | 61 | ok $repo->is_prepared_for_commits; 62 | ok $repo->is_prepared_for_push; 63 | ok $repo->is_synced_with_github; 64 | 65 | ok !$repo->git->is_empty; 66 | ok -e $repo->repo_dir->child("foo"); 67 | ok $repo->github->exists_on_github; 68 | sleep 1; 69 | ok $repo->are_git_and_github_on_the_same_commit; 70 | 71 | lives_ok { $repo->git->push; }; 72 | sleep 1; 73 | ok $repo->are_git_and_github_on_the_same_commit; 74 | }; 75 | 76 | 77 | subtest "Github & Git, Git is ahead" => sub { 78 | my $repo = new_repo; 79 | 80 | # Make a Github repo. 81 | my $github = $repo->github; 82 | $github->create_repo; 83 | 84 | # Put a commits into the Github repo 85 | my $git = Gitpan::Git->clone( 86 | repo_dir => $repo->repo_dir, 87 | url => $github->remote, 88 | distname => $repo->distname 89 | ); 90 | $git->repo_dir->child("foo")->touch; 91 | $git->add( "foo" ); 92 | $git->commit( message => "testing" ); 93 | $git->push; 94 | 95 | # Put another commit in the Git repo to get ahead 96 | $git->repo_dir->child("bar")->touch; 97 | $git->add( "bar" ); 98 | $git->commit( message => "getting ahead of Github" ); 99 | 100 | ok $repo->sync_with_github; 101 | 102 | ok $repo->is_synced_with_github; 103 | ok $repo->is_prepared_for_commits; 104 | ok $repo->is_prepared_for_push; 105 | 106 | ok !$repo->git->is_empty; 107 | ok -e $repo->repo_dir->child("foo"); 108 | ok $repo->github->exists_on_github; 109 | sleep 1; 110 | ok !$repo->are_git_and_github_on_the_same_commit; 111 | 112 | lives_ok { $repo->git->push; }; 113 | sleep 1; 114 | ok $repo->are_git_and_github_on_the_same_commit; 115 | }; 116 | 117 | 118 | subtest "Github + Git repos, Git is behind" => sub { 119 | my $repo = new_repo; 120 | 121 | # Make a Github repo. 122 | my $github = $repo->github; 123 | $github->create_repo; 124 | 125 | # Put a commit into the Github repo 126 | my $clone = Gitpan::Git->clone( 127 | url => $github->remote, 128 | distname => $repo->distname 129 | ); 130 | $clone->repo_dir->child("foo")->touch; 131 | $clone->add( "foo" ); 132 | $clone->commit( message => "testing" ); 133 | $clone->push; 134 | 135 | # Clone the Github repo so Repo will see it 136 | my $git = Gitpan::Git->clone( 137 | repo_dir => $repo->repo_dir, 138 | url => $github->remote, 139 | distname => $repo->distname 140 | ); 141 | 142 | # Put another commit in the Github repo to get ahead 143 | $clone->repo_dir->child("bar")->touch; 144 | $clone->add( "bar" ); 145 | $clone->commit( message => "getting ahead of Git" ); 146 | $clone->push; 147 | 148 | ok $repo->sync_with_github; 149 | 150 | ok $repo->is_synced_with_github; 151 | ok $repo->is_prepared_for_commits; 152 | ok $repo->is_prepared_for_push; 153 | 154 | ok !$repo->git->is_empty; 155 | ok -e $repo->repo_dir->child("foo"); 156 | ok -e $repo->repo_dir->child("bar"); 157 | ok $repo->github->exists_on_github; 158 | sleep 1; 159 | ok $repo->are_git_and_github_on_the_same_commit; 160 | 161 | lives_ok { $repo->git->push; }; 162 | sleep 1; 163 | ok $repo->are_git_and_github_on_the_same_commit; 164 | }; 165 | 166 | 167 | subtest "Github + Git repos, diverged" => sub { 168 | my $repo = new_repo; 169 | 170 | # Make a Github repo. 171 | my $github = $repo->github; 172 | $github->create_repo; 173 | 174 | # Repo will not see this 175 | my $clone = Gitpan::Git->clone( 176 | url => $github->remote, 177 | distname => $repo->distname 178 | ); 179 | 180 | # This is the one Repo will see 181 | my $git = Gitpan::Git->clone( 182 | repo_dir => $repo->repo_dir, 183 | url => $github->remote, 184 | distname => $repo->distname 185 | ); 186 | 187 | # Make Github diverge 188 | $clone->repo_dir->child("bar")->touch; 189 | $clone->add_all; 190 | $clone->commit( message => "Github is diverging" ); 191 | $clone->push; 192 | 193 | # And now the one Repo can see diverges 194 | $git->repo_dir->child("baz")->touch; 195 | $git->add_all; 196 | $git->commit( message => "local is diverging" ); 197 | 198 | throws_ok { 199 | $repo->sync_with_github; 200 | } qr{Not possible to fast-forward}; 201 | 202 | ok !$repo->is_synced_with_github; 203 | ok !$repo->is_prepared_for_commits; 204 | ok !$repo->is_prepared_for_push; 205 | 206 | sleep 1; 207 | ok !$repo->are_git_and_github_on_the_same_commit; 208 | }; 209 | 210 | 211 | done_testing; 212 | -------------------------------------------------------------------------------- /t/Test.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | note "Using test config"; { 8 | use Gitpan::ConfigFile; 9 | my $config_file = Gitpan::ConfigFile->new; 10 | my $config = $config_file->config; 11 | 12 | ok $config_file->is_test; 13 | is $config->github_owner, 'gitpan-test'; 14 | } 15 | 16 | note "The gitpan directory"; { 17 | use Gitpan::ConfigFile; 18 | my $config = Gitpan::ConfigFile->new->config; 19 | my $gitpan_dir = $config->gitpan_dir; 20 | 21 | ok -d $gitpan_dir, "gitpan directory is created"; 22 | 23 | # Check there are nothing but empty directories, because 24 | # Gitpan::Config will make them. 25 | my $iter = $gitpan_dir->iterator({ recurse => 1 }); 26 | while( my $path = $iter->() ) { 27 | ok -d $path; 28 | } 29 | } 30 | 31 | subtest "new_repo" => sub { 32 | { 33 | my $repo = new_repo; 34 | isa_ok $repo, "Gitpan::Repo"; 35 | } 36 | 37 | { 38 | my $repo = new_repo( distname => "Foo-Bar" ); 39 | isa_ok $repo, "Gitpan::Repo"; 40 | is $repo->distname, "Foo-Bar"; 41 | } 42 | 43 | { 44 | my $repo = new_repo( distname => "Foo-Bar" ); 45 | isa_ok $repo, "Gitpan::Repo"; 46 | is $repo->distname, "Foo-Bar"; 47 | } 48 | }; 49 | 50 | 51 | subtest "new_dist" => sub { 52 | { 53 | my $dist = new_dist; 54 | isa_ok $dist, "Gitpan::Dist"; 55 | } 56 | 57 | { 58 | my $dist = new_dist( distname => "Foo-Bar" ); 59 | isa_ok $dist, "Gitpan::Dist"; 60 | is $dist->distname, "Foo-Bar"; 61 | } 62 | 63 | { 64 | my $dist = new_dist( distname => "Foo-Bar" ); 65 | isa_ok $dist, "Gitpan::Dist"; 66 | is $dist->distname, "Foo-Bar"; 67 | } 68 | }; 69 | 70 | 71 | subtest "deleting a repo twice" => sub { 72 | { 73 | my $repo1 = new_repo; 74 | $repo1->github->create_repo; 75 | 76 | my $repo2 = new_repo( 77 | distname => $repo1->distname, 78 | overwrite => 0, 79 | ); 80 | 81 | # Make sure the repo is actually created. 82 | sleep 1; 83 | } 84 | 85 | pass("new_repo() won't try to delete the same repo twice"); 86 | }; 87 | 88 | done_testing; 89 | -------------------------------------------------------------------------------- /t/Types.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | use Test::TypeConstraints; 7 | use Gitpan::Types; 8 | 9 | note "Path types"; { 10 | type_isa "/foo/bar/baz.txt", Path, "str to file coercion", coerce => sub { 11 | isa_ok $_[0], "Path::Tiny"; 12 | is $_[0], "/foo/bar/baz.txt"; 13 | }; 14 | 15 | type_isa "/what/stuff", Path, "str to dir coercion", coerce => sub { 16 | isa_ok $_[0], "Path::Tiny"; 17 | is $_[0], "/what/stuff"; 18 | }; 19 | 20 | type_isa "/up/down/left", AbsPath, "str to absdir coercion", coerce => sub { 21 | isa_ok $_[0], "Path::Tiny"; 22 | is $_[0], "/up/down/left"; 23 | }; 24 | 25 | type_isnt "this/that", AbsPath; 26 | } 27 | 28 | note "Dist and module types"; { 29 | type_isa "Foo-Bar", DistName; 30 | type_isa "Foo", DistName; 31 | type_isnt "Foo::Bar", DistName; 32 | type_isnt "", DistName; 33 | type_isnt " ", DistName; 34 | 35 | type_isa "Foo::Bar", ModuleName; 36 | type_isa "Foo", ModuleName; 37 | type_isnt "123::Foo", ModuleName; 38 | } 39 | 40 | note "URI coercion"; { 41 | type_isa "http://example.com", URI, "str to URI coercion", coerce => sub { 42 | isa_ok $_[0], "URI"; 43 | is $_[0], "http://example.com"; 44 | }; 45 | } 46 | 47 | note "Extra URI methods"; { 48 | require URI; 49 | is "URI"->new("http://example.com/foo/")->append_path("/blah/blat"), 50 | "http://example.com/foo/blah/blat"; 51 | 52 | is "URI"->new("http://example.com/foo")->append_path("blah/blat"), 53 | "http://example.com/foo/blah/blat"; 54 | } 55 | 56 | done_testing; 57 | -------------------------------------------------------------------------------- /t/backpan/authors/01mailrc.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/01mailrc.txt.gz -------------------------------------------------------------------------------- /t/backpan/authors/02authors.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/02authors.txt.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/A/AA/AANOAA/Acme-eng2kor-0.0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/A/AA/AANOAA/Acme-eng2kor-0.0.1.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/A/AA/AANOAA/Acme-eng2kor-v0.0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/A/AA/AANOAA/Acme-eng2kor-v0.0.2.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/A/AQ/AQUILINA/Acme-Warn-LOLCAT-0.01.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/A/AQ/AQUILINA/Acme-Warn-LOLCAT-0.01.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/A/AQ/AQUILINA/Acme-Warn-LOLCAT-0.02.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/A/AQ/AQUILINA/Acme-Warn-LOLCAT-0.02.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.04.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.04.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.05.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/Date-Spoken-German-0.05.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/date-spoken-german-0.02.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/date-spoken-german-0.02.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/date-spoken-german-0.03.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/C/CH/CHRWIN/date-spoken-german/date-spoken-german-0.03.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/C/CN/CNANDOR/Bundle-Slash-2.11.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/C/CN/CNANDOR/Bundle-Slash-2.11.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.1.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DC/DCANTRELL/Acme-Pony-1.1.2.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.001-03-09-2009.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.001-03-09-2009.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.002.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.002.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.0021.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.0021.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.0022.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh-0.0022.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/D/DH/DHOSS/Acme-Blarghy-McBlarghBlargh.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.001.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.001.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.002.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.002.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.003.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.003.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.004.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.004.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.005.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.005.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.006.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/E/ET/ETHER/Acme-LookOfDisapproval-0.006.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.90.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.90.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.91.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.91.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.92.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.92.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.93.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.93.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.94-TRIAL.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.94-TRIAL.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.95.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.95.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.96.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.96.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.97.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.97.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.98.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.98.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.99.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/F/FL/FLORA/Math-BigInt-1.99.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/H/HK/HKOCH/reform-0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/H/HK/HKOCH/reform-0.1.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/H/HK/HKOCH/reform-0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/H/HK/HKOCH/reform-0.2.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/H/HK/HKOCH/reform-0.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/H/HK/HKOCH/reform-0.3.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/J/JE/JESSE/Acme-Buffy-1.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/J/JE/JESSE/Acme-Buffy-1.3.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/J/JP/JPEACOCK/Math-BigInt-0.01.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/J/JP/JPEACOCK/Math-BigInt-0.01.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/K/KE/KENFOX/perl-c++-api.0.0_2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/K/KE/KENFOX/perl-c++-api.0.0_2.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/K/KE/KENFOX/perl-c++-api.0.0_3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/K/KE/KENFOX/perl-c++-api.0.0_3.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.1.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.2.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.3.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.4.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.5.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.6.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LB/LBROCARD/Acme-Buffy-1.6.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LD/LDACHARY/URIC-2.02.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LD/LDACHARY/URIC-2.02.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LD/LDACHARY/uri-2.00.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LD/LDACHARY/uri-2.00.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/L/LD/LDACHARY/uri-2.01.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/L/LD/LDACHARY/uri-2.01.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/M/MA/MACGYVER/SMTP-Server-1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/M/MA/MACGYVER/SMTP-Server-1.0.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/M/MA/MACGYVER/SMTP-Server-1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/M/MA/MACGYVER/SMTP-Server-1.1.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/M/MA/MARCEL/-0.01.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/M/MA/MARCEL/-0.01.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/M/MA/MAXA/Acme-Maybe-0.01.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/M/MA/MAXA/Acme-Maybe-0.01.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/M/MA/MAXA/Acme-Maybe-0.02.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/M/MA/MAXA/Acme-Maybe-0.02.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PA/PAWAPAWA/Lingua-JA-WordNet-0.21.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PA/PAWAPAWA/Lingua-JA-WordNet-0.21.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.99.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.99.1.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.9902.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.9902.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.991.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.991.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.992.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.992.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.993.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.993.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.997.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.997.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.9993.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.9993.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.99_05.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-1.99_05.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/R/RF/RFREIMUTH/RandomJungle-0.05.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/R/RF/RFREIMUTH/RandomJungle-0.05.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/S/SH/SHARYANTO/ReForm-0.00.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/S/SH/SHARYANTO/ReForm-0.00.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.35.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.35.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.36.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.36.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.37.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.37.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.38.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.38.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.39.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.39.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.40.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.40.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.41.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.41.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.42.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.42.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.43.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.43.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.44.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.44.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.45.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.45.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.46.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.46.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.47.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.47.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.48.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.48.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.49.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.49.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.53.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.53.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.54.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.54.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.55.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.55.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.56.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/Math-BigInt-1.56.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.57.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.57.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.58.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.58.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.59.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.59.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.60.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.60.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.61.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.61.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.62.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.62.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.63.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.63.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.64.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.65.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.65.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.66.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.66.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.67.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.67.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.68.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.68.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.69.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.69.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.70.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.70.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.71.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.71.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.72.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.72.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.73.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.73.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.74.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.74.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.75.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.75.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.76.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.76.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.77.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.77.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.78.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.78.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.79.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.79.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.80.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.80.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.81.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.81.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.82.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.82.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.83.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.83.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.84.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.84.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.85.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.85.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.86.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.86.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.87.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.87.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.88.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.88.tar.gz -------------------------------------------------------------------------------- /t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.89.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/authors/id/T/TE/TELS/math/Math-BigInt-1.89.tar.gz -------------------------------------------------------------------------------- /t/backpan/backpan-index.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/backpan-index.gz -------------------------------------------------------------------------------- /t/backpan/modules/02packages.details.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evalEmpire/gitpan/b56f5d778d2ae26ec60976ae85db7763a71862cf/t/backpan/modules/02packages.details.txt.gz -------------------------------------------------------------------------------- /t/import/Math-BigInt.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This distribution has proven a problem in the past. 4 | 5 | use lib 't/lib'; 6 | use Gitpan::perl5i; 7 | use Gitpan::Test; 8 | 9 | use Gitpan::Dist; 10 | 11 | my $DistName = 'Math-BigInt'; 12 | 13 | note "Import $DistName"; { 14 | my $dist = Gitpan::Dist->new( 15 | distname => $DistName 16 | ); 17 | $dist->delete_repo(wait => 1); 18 | 19 | cmp_deeply $dist->versions_to_import, [qw( 20 | 0.01 21 | 1.35 22 | 1.36 23 | 1.37 24 | 1.38 25 | 1.39 26 | 1.40 27 | 1.41 28 | 1.42 29 | 1.43 30 | 1.44 31 | 1.45 32 | 1.46 33 | 1.47 34 | 1.48 35 | 1.49 36 | 1.53 37 | 1.54 38 | 1.55 39 | 1.56 40 | 1.57 41 | 1.58 42 | 1.59 43 | 1.60 44 | 1.61 45 | 1.62 46 | 1.63 47 | 1.64 48 | 1.65 49 | 1.66 50 | 1.67 51 | 1.68 52 | 1.69 53 | 1.70 54 | 1.71 55 | 1.72 56 | 1.73 57 | 1.74 58 | 1.75 59 | 1.76 60 | 1.77 61 | 1.78 62 | 1.79 63 | 1.80 64 | 1.81 65 | 1.82 66 | 1.83 67 | 1.84 68 | 1.85 69 | 1.86 70 | 1.87 71 | 1.88 72 | 1.89 73 | 1.90 74 | 1.91 75 | 1.92 76 | 1.93 77 | 1.94-TRIAL 78 | 1.95 79 | 1.96 80 | 1.97 81 | 1.98 82 | 1.99 83 | 1.99.1 84 | 1.9902 85 | 1.99_05 86 | 1.991 87 | 1.992 88 | 1.993 89 | 1.997 90 | 1.9993 91 | )]; 92 | 93 | $dist->import_releases( 94 | before_import => method($release) { 95 | note "Importing ".$release->version; 96 | }, 97 | push => 0, 98 | ); 99 | } 100 | 101 | done_testing; 102 | -------------------------------------------------------------------------------- /t/import/bad_links.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This distribution has dangling symbolic links 4 | 5 | use lib 't/lib'; 6 | use Gitpan::perl5i; 7 | use Gitpan::Test; 8 | 9 | use Gitpan::Dist; 10 | 11 | my $DistName = 'SMTP-Server'; 12 | 13 | note "Import $DistName"; { 14 | my $dist = Gitpan::Dist->new( 15 | distname => $DistName 16 | ); 17 | $dist->delete_repo(wait => 1); 18 | 19 | $dist->import_releases( 20 | before_import => method($release) { 21 | note "Importing ".$release->short_path; 22 | }, 23 | push => 0, 24 | ); 25 | 26 | cmp_deeply $dist->repo->git->releases, [ 27 | "MACGYVER/SMTP-Server-1.0.tar.gz", 28 | "MACGYVER/SMTP-Server-1.1.tar.gz", 29 | ]; 30 | } 31 | 32 | done_testing; 33 | -------------------------------------------------------------------------------- /t/import/multiple_releases_same_version.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Dist; 8 | 9 | my $DistName = 'Acme-Buffy'; 10 | 11 | note "Import $DistName"; { 12 | my $dist = Gitpan::Dist->new( distname => 'Acme-Buffy' ); 13 | $dist->delete_repo( wait => 1 ); 14 | 15 | isnt $dist->releases_to_import->size, 0; 16 | note "Importing ".$dist->paths_to_import->join(", "); 17 | 18 | $dist->import_releases( push => 0 ); 19 | 20 | cmp_deeply $dist->repo->releases, [ 21 | "JESSE/Acme-Buffy-1.3.tar.gz", 22 | "LBROCARD/Acme-Buffy-1.1.tar.gz", 23 | "LBROCARD/Acme-Buffy-1.2.tar.gz", 24 | "LBROCARD/Acme-Buffy-1.3.tar.gz", 25 | "LBROCARD/Acme-Buffy-1.4.tar.gz", 26 | "LBROCARD/Acme-Buffy-1.5.tar.gz", 27 | "LBROCARD/Acme-Buffy-1.6.tar.gz", 28 | ]; 29 | } 30 | 31 | done_testing; 32 | -------------------------------------------------------------------------------- /t/import/normalization.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Dist; 8 | 9 | subtest "dist name normalization" => sub { 10 | my $dist = Gitpan::Dist->new( distname => "URIC" ); 11 | my $v200 = $dist->release_from_version("2.00"); 12 | my $v202 = $dist->release_from_version("2.02"); 13 | 14 | is $v200->short_path, 'LDACHARY/uri-2.00.tar.gz'; 15 | is $v202->short_path, 'LDACHARY/URIC-2.02.tar.gz'; 16 | }; 17 | 18 | subtest "release normalization" => sub { 19 | my $dist = Gitpan::Dist->new( distname => "Bi"); 20 | 21 | my $release = $dist->release_from_version("0.01"); 22 | is $release->short_path, "MARCEL/-0.01.tar.gz"; 23 | }; 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/import/perl-c\+\+-api.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This distribution has proven a problem in the past. 4 | 5 | use lib 't/lib'; 6 | use Gitpan::perl5i; 7 | use Gitpan::Test; 8 | 9 | use Gitpan::Dist; 10 | 11 | my $DistName = 'perl-c++-api'; 12 | 13 | note "Import $DistName"; { 14 | my $dist = Gitpan::Dist->new( 15 | distname => $DistName 16 | ); 17 | $dist->delete_repo( wait => 1 ); 18 | 19 | cmp_deeply $dist->versions_to_import, ['0.0_2', '0.0_3']; 20 | 21 | $dist->import_releases( push => 0 ); 22 | 23 | my($last_commit) = $dist->git->log("-1"); 24 | like $last_commit->message, qr{^gitpan-cpan-maturity:\s+developer}ms; 25 | } 26 | 27 | done_testing; 28 | -------------------------------------------------------------------------------- /t/import/simple_single_dist.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Dist; 8 | 9 | my $DistName = 'Acme-LookOfDisapproval'; 10 | 11 | note "Import $DistName"; { 12 | my $dist = Gitpan::Dist->new( 13 | distname => $DistName 14 | ); 15 | $dist->delete_repo( wait => 1 ); 16 | 17 | isnt $dist->versions_to_import->size, 0; 18 | note "Importing ".$dist->versions_to_import->join(", "); 19 | 20 | $dist->import_releases( push => 0 ); 21 | 22 | cmp_deeply $dist->versions_to_import, []; 23 | } 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/import/update.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This distribution has proven a problem in the past. 4 | 5 | use lib 't/lib'; 6 | use Gitpan::perl5i; 7 | use Gitpan::Test; 8 | 9 | use Gitpan::Dist; 10 | 11 | my $DistName = 'Acme-LookOfDisapproval'; 12 | 13 | subtest "Import $DistName and update" => sub { 14 | my $dist = Gitpan::Dist->new( 15 | distname => $DistName 16 | ); 17 | $dist->delete_repo( wait => 1 ); 18 | 19 | my $to_import = $dist->releases_to_import; 20 | cmp_ok @$to_import, '>=', 4; 21 | 22 | my $repo = $dist->repo; 23 | note "Importing and pushing first batch"; 24 | my $first_releases = [shift @$to_import, shift @$to_import]; 25 | ok $repo->import_releases( 26 | releases => $first_releases 27 | ); 28 | 29 | note "Importing and pushing second batch"; 30 | my $second_releases = [shift @$to_import, shift @$to_import]; 31 | ok $repo->import_releases( 32 | releases => $second_releases 33 | ); 34 | 35 | $dist->delete_repo; 36 | }; 37 | 38 | done_testing; 39 | -------------------------------------------------------------------------------- /t/import/version_normalization.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Dist; 8 | 9 | # Acme-eng2kor is also a test for authors with no email address. 10 | subtest "version normalization" => sub { 11 | my $dist = Gitpan::Dist->new( 12 | distname => 'Acme-eng2kor' 13 | ); 14 | $dist->delete_repo( wait => 1 ); 15 | 16 | $dist->import_releases( 17 | before_import => method($release) { 18 | note "Importing ".$release->version; 19 | }, 20 | push => 0, 21 | ); 22 | 23 | my $git = $dist->git; 24 | 25 | cmp_deeply $git->cpan_versions, [ "0.0.1", "v0.0.2" ]; 26 | cmp_deeply $git->gitpan_versions, [ "0.0.1", "0.0.2" ]; 27 | cmp_deeply $git->cpan_paths, [ 28 | "AANOAA/Acme-eng2kor-0.0.1.tar.gz", 29 | "AANOAA/Acme-eng2kor-v0.0.2.tar.gz" 30 | ]; 31 | }; 32 | 33 | done_testing; 34 | -------------------------------------------------------------------------------- /t/import/with_git_dir.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | use Gitpan::Test; 6 | 7 | use Gitpan::Dist; 8 | 9 | my $DistName = 'Acme-Warn-LOLCAT'; 10 | 11 | note "Import $DistName"; { 12 | my $dist = Gitpan::Dist->new( distname => $DistName ); 13 | $dist->delete_repo( wait => 1 ); 14 | 15 | isnt $dist->releases_to_import->size, 0; 16 | note "Importing ".$dist->paths_to_import->join(", "); 17 | 18 | $dist->import_releases( push => 0 ); 19 | 20 | cmp_deeply $dist->repo->releases, [ 21 | "AQUILINA/Acme-Warn-LOLCAT-0.01.tar.gz", 22 | "AQUILINA/Acme-Warn-LOLCAT-0.02.tar.gz", 23 | ]; 24 | } 25 | 26 | done_testing; 27 | -------------------------------------------------------------------------------- /t/lib/Gitpan/Test.pm: -------------------------------------------------------------------------------- 1 | package Gitpan::Test; 2 | 3 | use Gitpan::perl5i; 4 | use Gitpan::ConfigFile; 5 | 6 | use Import::Into; 7 | 8 | use Test::Most (); 9 | 10 | method import($class: ...) { 11 | my $caller = caller; 12 | 13 | warn "GITPAN_GITHUB_ACCESS_TOKEN is not set, tests which talk to Github will probably fail" unless $ENV{GITPAN_GITHUB_ACCESS_TOKEN}; 14 | 15 | $ENV{GITPAN_CONFIG_DIR} //= "."->path->absolute; 16 | $ENV{GITPAN_TEST} //= 1; 17 | 18 | Test::Most->import::into($caller); 19 | 20 | # Clean up and recreate the gitpan directory 21 | my $gitpan_dir = Gitpan::ConfigFile->new->config->gitpan_dir; 22 | croak "The gitpan directory used for testing ($gitpan_dir) is outside the test tree, refusing to delete it" 23 | if !"t"->path->subsumes($gitpan_dir); 24 | $gitpan_dir->remove_tree({safe => 0}); 25 | $gitpan_dir->mkpath; 26 | 27 | (\&new_repo)->alias( $caller.'::new_repo' ); 28 | (\&new_dist)->alias( $caller.'::new_dist' ); 29 | (\&rand_distname)->alias( $caller.'::rand_distname' ); 30 | (\&test_runtime)->alias( $caller.'::test_runtime' ); 31 | 32 | return; 33 | } 34 | 35 | 36 | { 37 | package Gitpan::Dist::SelfDestruct; 38 | 39 | use Gitpan::perl5i; 40 | use Gitpan::OO; 41 | 42 | extends 'Gitpan::Dist'; 43 | 44 | method DESTROY { 45 | eval { 46 | $self->delete_repo; 47 | }; 48 | 49 | return; 50 | } 51 | } 52 | 53 | 54 | { 55 | package Gitpan::Repo::SelfDestruct; 56 | 57 | use Gitpan::perl5i; 58 | use Gitpan::OO; 59 | 60 | extends 'Gitpan::Repo'; 61 | 62 | method DESTROY { 63 | eval { 64 | $self->delete_repo; 65 | }; 66 | 67 | return; 68 | } 69 | } 70 | 71 | 72 | func rand_distname { 73 | my @names; 74 | 75 | my @letters = ("a".."z","A".."Z"); 76 | for (0..rand(4)+1) { 77 | push @names, join "", map { $letters[rand @letters] } 1..rand(20); 78 | } 79 | 80 | return @names->join("-"); 81 | } 82 | 83 | 84 | func new_dist_or_repo( $class!, %params ) { 85 | my $overwrite = delete $params{overwrite} // 1; 86 | 87 | # If we're using a random dist name, no need to check 88 | # if it already exists. 89 | if( !defined $params{distname} ) { 90 | $params{distname} = rand_distname; 91 | $overwrite = 0; 92 | } 93 | 94 | my $obj = $class->new( %params ); 95 | $obj->delete_repo( wait => 1 ) if $overwrite; 96 | 97 | return $obj; 98 | } 99 | 100 | 101 | func new_repo(...) { 102 | return new_dist_or_repo( "Gitpan::Repo::SelfDestruct", @_ ); 103 | } 104 | 105 | 106 | func new_dist(...) { 107 | return new_dist_or_repo( "Gitpan::Dist::SelfDestruct", @_ ); 108 | } 109 | 110 | 111 | use Time::HiRes qw(gettimeofday); 112 | func test_runtime( 113 | CodeRef :$code!, 114 | Num :$time!, 115 | Num :$delta = 0.1 116 | ) { 117 | my $start_time = gettimeofday; 118 | $code->(); 119 | my $end_time = gettimeofday; 120 | 121 | my $time_spent = $end_time - $start_time; 122 | 123 | local $Test::Builder::Level = $Test::Builder::Level + 1; 124 | Test::More::cmp_ok( 125 | abs($time_spent - $time), "<=", $delta, 126 | "$time_spent expected about $time" 127 | ); 128 | } 129 | 130 | 131 | 1; 132 | -------------------------------------------------------------------------------- /t/perl5i.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 't/lib'; 4 | use Gitpan::perl5i; 5 | 6 | use Gitpan::Test; 7 | 8 | note "Signatures"; { 9 | func with_signature( Str $msg = "default" ) { 10 | return $msg; 11 | } 12 | 13 | is with_signature, "default"; 14 | } 15 | 16 | note "perl5i"; { 17 | is [1,2,3]->join(""), "123"; 18 | } 19 | 20 | done_testing; 21 | --------------------------------------------------------------------------------