Configurarea unei oglinzi locale pentru pachetele de compozitori cu Satis

Instalarea tuturor bibliotecilor dvs. PHP cu Composer este o modalitate excelentă de a economisi timp. Dar proiectele mai mari testate și executate automat la fiecare comitere a sistemului de control al versiunilor software (SVC) va dura mult timp pentru a instala toate pachetele necesare de pe Internet. Doriți să executați testele dvs. cât mai curând posibil prin intermediul sistemului de integrare continuă (CI), astfel încât să aveți feedback rapid și reacții rapide la eșec. În acest tutorial vom configura o oglindă locală pentru a vă proxy toate pachetele necesare în proiectul dvs. composer.json fişier. Acest lucru va face munca noastră de CI mult mai rapidă, să instaleze pachetele în rețeaua locală sau chiar să găzduiască pe aceeași mașină și să ne asigurăm că versiunile specifice ale pachetelor sunt întotdeauna disponibile.


Ce este Satis?

Satis este numele aplicației pe care o vom folosi pentru a reflecta diferitele depozite pentru proiectul nostru. Se situează ca un proxy între Internet și compozitorul tău. Soluția noastră va crea o oglindă locală a câtorva pachete și vom instrui compozitorul să îl folosească în locul surselor găsite pe Internet.

Iată o imagine care spune mai mult de o mie de cuvinte.


Proiectul nostru va folosi compozitorul ca de obicei. Acesta va fi configurat să utilizeze serverul local Satis ca sursă primară. Dacă un pachet se găsește acolo, acesta va fi instalat de acolo. Dacă nu, vom lăsa compozitorul să folosească pachetul default pentru a prelua pachetul.


Satisfacție

Satis este disponibil prin compozitor, așadar instalarea este foarte simplă. În arhiva codului sursă atașat, veți găsi Satis instalat în Surse / Satis pliant. Mai întâi vom instala chiar compozitorul.

$ curl -sS https://getcomposer.org/installer | php #! / usr / bin / env php Toate setările sunt corecte pentru utilizarea Composer Downloading ... Compozitor instalat cu succes la: / home / csaba / Personal / Programare / NetTuts / Setarea unei oglinzi locale pentru pachetele Composer cu Satis / Sources / Satis / .phar Folosiți-l: php composer.phar

Apoi vom instala Satis.

$ php compiler.phar crea-project compozitor / satis -stability = dev --keep-vcs Instalarea compozitorului / satis (dev-master eddb78d52e8f7ea772436f2320d6625e18d5daf5) - Instalarea compozitorului / satis (dev master master) csaba / Personal / Programare / NetTuts / Configurarea unei mirror-uri locale pentru pachetele Composer cu Satis / Sources / Satis / satis Încărcarea repositorelor compozitorilor cu informații despre pachete Instalarea dependențelor (inclusiv requ-dev) 27b0fc6) Clonarea 27b0fc645a557b2fc7bc7735cfb05505de9351be - Instalarea simfony / finder (v2.4.0-BETA1) Descărcarea: 100% - Instalarea simfony / consolă (dev-master f44cc6f) Clonarea f44cc6fabdaa853335d7f54f1b86c99622db518a - Instalarea seldon / jsonlint / json-schema (1.1.0) Descărcarea: 100% - Instalarea compozitorului / compozitorului (dev-master f8be812) Clonarea f8be812a496886c84918d6dd1b50db5c16da3cc3 - 100% symfony / console sugerează instalarea simfony / event-dispatcher () Generarea fișierelor autoload

Configurarea Satis

Satis este configurat de un fișier JSON foarte asemănător cu cel al compozitorului. Puteți folosi orice nume doriți pentru fișierul dvs. și îl puteți specifica mai târziu. Noi vom folosi "oglindite-packages.conf".

"nume": "NetTuts Composer Mirror", "pagina de pornire": "http: // localhost: 4680", "depozite": ["tip": https: // github. com / SyntoNet / monolog ", " type ":" compozitor "," url ":" https://packagist.org "]," necesită ": monolog / monolog" "batjocură / batjocură": "*", "phpunit / phpunit": "*", "necesită dependențe"

Să analizăm acest fișier de configurare.

  • Nume - reprezintă un șir care va fi afișat pe interfața web a oglinzii noastre.
  • pagina principala - este adresa web unde vor fi păstrate pachetele noastre. Acest lucru nu ne spune serverului nostru web să utilizeze acea adresă și port, ci doar informații despre o configurație de lucru. Vom configura accesul la acel adresă și port mai târziu.
  • arhive - o listă de depozite ordonate după preferință. În exemplul nostru, primul depozit este o furculiță Github a bibliotecilor de logare monolog. Ea are unele modificări și vrem să folosim acea furculiță specifică când montează monolog. Tipul acestui depozit este "vcs"Al doilea depozit este de tip"compozitor"URL-ul său este site-ul implicit ambalat.org.
  • necesita - listează pachetele pe care dorim să le reflectăm. Acesta poate reprezenta un pachet specific cu o versiune sau o ramură specifică, sau orice versiune care contează. Utilizează aceeași sintaxă ca și "necesita"sau"necesită-dev"în dvs. composer.json.
  • necesită-dependențe - este opțiunea finală din exemplul nostru. Acesta va spune Satis să oglindească nu numai pachetele pe care le-am specificat în "necesita", dar și toate dependențele acestora.

Pentru a încerca rapid setările noastre, trebuie mai întâi să-i spunem lui Satis să creeze oglinzile. Rulați această comandă în dosarul în care ați instalat Satis.

$ php ./satis/bin/satis build ./mirrored-packages.conf ./packages-mirror Pachete de scanare Scrierea pachetelor jjson Scrierea paginii web

În timp ce procesul are loc, veți vedea cum Satis reflectă fiecare versiune găsită a pachetelor necesare. Aveți răbdare să dureze ceva timp pentru a construi toate aceste pachete.

Satis cere asta date.timezone care urmează să fie specificate în php.ini dosar, asigurați-vă că este și setați la fusul orar local. În caz contrar, va apărea o eroare.

[Twig_Error_Runtime] O excepție a fost aruncată în timpul redării unui șablon ("date_default_timezone_get (): Nu este sigur să te bazezi pe setările fusului orar al sistemului. Ești obligat să folosești setarea date.timezone sau data_default_timezone_set.

Apoi, putem rula o instanță de server PHP în consola noastră, îndreptându-ne către repozitoriul recent creat. PHP 5.4 sau mai nou este necesar.

$ php -S localhost: 4680 -t ./packages-mirror/ PHP 5.4.22-pl0-gentoo Server de dezvoltare a început la Sun Dec 8 14:47:48 2013 Ascultați pe http: // localhost: 4680 Rootul documentului este / home / csaba / Personal / Programare / NetTuts / Configurarea unei oglinzi locale pentru pachetele Composer cu Satis / Sources / Satis / packages-mirror Apăsați pe Ctrl-C pentru a renunța. [Sun Dec 8 14:48:09 2013] 127.0.0.1:56999 [200]: / [Sun Dec 8 14:48:09 2013] 127.0.0.1:57000 [404]: /favicon.ico - Nu există un astfel de fișier sau director

Și acum putem căuta pachetele noastre în oglindă și chiar căutăm anumite, indicând browserul nostru web http: // localhost: 4680:



Să o găzduim pe Apache

Dacă aveți o Apache la îndemână, crearea unei gazde virtuale pentru Satis va fi destul de simplă.

Ascultă 4680  Opțiuni -Indexes FollowSymLinks AllowOverride toate Ordinul permite, respinge Permiteți tuturor   DocumentRoot "/ path / to / your / packages / mirror" Numele serverului 127.0.0.1:4680 ServerAdmin [email protected] ErrorLog syslog: user 

Folosim doar a .conf fișier ca acesta, pus în Apache conf.d dosar, de obicei /etc/apache2/conf.d. Creează o gazdă virtuală pe portul 4680 și o arată spre dosarul nostru. Desigur, puteți utiliza portul dorit.


Actualizarea Oglinzilor noastre

Satis nu poate actualiza automat oglinzile decât dacă le spunem. Deci, cel mai simplu mod, pe orice sistem de tip UNIX, este doar să adăugați un job cron în sistemul dvs. Asta ar fi foarte simplu, și doar un script simplu pentru a executa comanda noastră de actualizare.

# / bin / bash php / full / cale / către / satis / bin / satis construi \ /full/path/to/mirrored-packages.conf \ / full / path / to / packages-mirror

Neajunsul acestei soluții este că este static. Trebuie să actualizăm manual oglindite-packages.conf de fiecare dată când adăugăm un alt pachet la proiectul nostru composer.json. Dacă sunteți parte dintr-o echipă dintr-o companie cu un proiect mare și un server de integrare continuă, nu vă puteți baza pe faptul că oamenii își amintesc să adauge pachetele pe server. Este posibil ca aceștia să nu aibă permisiuni de acces la infrastructura CI.


Actualizarea dinamic a configurației Satis

Este timpul pentru un exercițiu PHP TDD. Dacă doriți doar ca codul să fie gata și să fie difuzat, verificați codul sursă atașat acestui tutorial.

requ_once __DIR__. "/ ... / ... / ... / ... / vendor/autoload.php"; clasa SatisUpdaterTest extinde PHPUnit_Framework_TestCase function testBehavior () $ this-> assertTrue (true); 

Ca de obicei, începem cu un test degenerativ, suficient pentru a ne asigura că avem un cadru de testare de lucru. S-ar putea să observați că am o linie destul de ciudată looking_once, asta pentru că vreau să evit să trebuiască să reinstalez PHPUnit și Mockery pentru fiecare proiect mic. Așa că le am într-un furnizor dosar în meu NetTuts"rădăcină. Ar trebui doar să le instalați cu compozitor și să renunțați require_once line cu totul.

clasa SatisUpdaterTest extinde PHPUnit_Framework_TestCase function testDefaultConfigFile () $ expected = '"name": "NetTuts Composer Mirror", "homepage": "http: // localhost: 4680" vcs "," url ":" https://github.com/SynetoNet/monolog ", " type ":" compozitor "," url ":" https://packagist.org "] : , "necesită dependențe": true '; $ actual = $ this-> parseComposerConf ("); $ this-> assertEquals ($ așteptat, $ actual);

Asta pare corect. Toate câmpurile, cu excepția "necesita"sunt statice.Avem nevoie de a genera doar pachete.Articolele sunt de orientare spre clonele noastre git private și la ambalator, după cum este necesar.Gestionarea acestora este mai mult de un loc de muncă sysadmin decât un dezvoltator de software.

Desigur, acest lucru nu reușește cu:

PHP Eroare fatală: Apel la metoda nedefinită SatisUpdaterTest :: parseComposerConf ()

Fixarea este ușoară.

funcția privată parseComposerConf ($ string) 

Tocmai am adăugat o metodă goală, cu numele necesar, ca privat, la clasa noastră de test. Cool, dar acum avem o altă eroare.

PHPUnit_Framework_ExpectationFailedException: Nu a reușit să se afirme că se așteaptă meciuri null '...'

Deci, null nu se potrivește cu șirul nostru care conține toate configurațiile implicite.

Funcția privată parseComposerConf ($ string) returnează "nume": "NetTuts Composer Mirror", "homepage": "http: // localhost: 4680" url ":" https://github.com/SynetoNet/monolog ", " type ":" compozitor "," url ":" https://packagist.org "] "necesități-dependențe": true '; 

Bine, asta funcționează. Toate testele trec.

PHPUnit 3.7.28 de Sebastian Bergmann. Timp: 15 ms, Memorie: 2.50Mb OK (1 test, 1 afirmație)

Dar am introdus o dublare oribilă. Tot textul static în două locuri, scris personaj după caracter în două locuri diferite. Să o rezolvăm:

class SatisUpdaterTest extinde PHPUnit_Framework_TestCase static $ DEFAULT_CONFIG = „ "name": "NetTuts Composer Mirror", "prima pagina": "http: // localhost: 4680", "arhive": [ "type": "vcs"," url ":" https://github.com/SynetoNet/monolog ", " type ":" compozitor "," url ":" https://packagist.org "] "necesități-dependențe": true '; funcția testDefaultConfigFile () $ expected = self :: $ DEFAULT_CONFIG; $ actual = $ this-> parseComposerConf ("); $ this-> assertEquals ($ expect, $ actual); funcția privată parseComposerConf ($ string) return self = :: DEFAULT_CONFIG;

Ahhh! Asa e mai bine.

 funcția testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration () $ expected = self :: $ DEFAULT_CONFIG; $ actual = $ this-> parseComposerConf ('"cer": '); $ this-> assertEquals ($ așteptat, $ actual); 

Bine. Și asta trece. Dar, de asemenea, subliniază o anumită dublare și o sarcină inutilă.

 Funcția testDefaultConfigFile () $ real = $ this-> parseComposerConf ( "); $ this-> assertEquals (self :: $ DEFAULT_CONFIG, $ real); funcția testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration () $ real = $ this-> parseComposerConf ( ' "cer":  '; $ this-> assertEquals (auto :: $ DEFAULT_CONFIG, $ actual);

Am insistat $ așteptat variabil. $ reală ar putea fi, de asemenea, inline, dar îmi place mai bine în acest fel. Se concentrează pe ceea ce este testat.

Acum avem o altă problemă. Următorul test pe care vreau să-l scriu ar arăta astfel:

testul funcțieiARequiredPackageInComposerWillBeInSatisAlso () $ actual = $ this-> parseComposerConf ('"necesită": "Mockery / Mockery": "> = 0.7.2"'); $ this-> assertContains ("Mockery / Mockery": "> = 0.7.2" ', $ actual); 

Dar după ce am scris implementarea simplă, vom observa că este nevoie json_decode () și json_encode (). Și, bineînțeles, aceste funcții reformate șirul nostru și corzile potrivite vor fi greu în cel mai bun caz. Trebuie să facem un pas înapoi.

Funcția testDefaultConfigFile () $ real = $ this-> parseComposerConf ( "); $ this-> assertJsonStringEqualsJsonString ($ this-> jsonRecode (self :: $ DEFAULT_CONFIG), $ real); funcția testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration () $ real = $ this-> parseComposerConf ( ' "necesită": '); $ this-> assertJsonStringEqualsJsonString ($ this-> jsonRecode (self :: $ DEFAULT_CONFIG), $ reală); funcția parseComposerConf privat ($ jsonConfig) întoarcere $ this-> jsonRecode (auto :: $ DEFAULT_CONFIG); funcția privată jsonRecode ($ json) returnează json_encode (json_decode ($ json, true));

Am schimbat metoda noastră de afirmație pentru a compara șirurile JSON și am recodificat și noi $ reală variabil. ParseComposerConf () a fost de asemenea modificată pentru a utiliza această metodă. Veți vedea într-un moment cum ne ajută. Următorul nostru test devine mai specific JSON.

testul funcțieiARequiredPackageInComposerWillBeInSatisAlso () $ actual = $ this-> parseComposerConf ('"necesită": "Mockery / Mockery": "> = 0.7.2"'); $ this-> assertEquals ('> = 0.7.2', json_decode ($ actual, true) ['necesită']] ['Impuscaturi / batjocură']]; 

Și efectuarea acestui test, împreună cu restul testelor, este destul de ușor, din nou.

funcția privată parseComposerConf ($ jsonConfig) $ addedConfig = json_decode ($ jsonConfig, true); $ config = json_decode (auto :: $ DEFAULT_CONFIG, true); dacă (isset ($ addedConfig ['necesită'])) $ config ['require'] = $ addedConfig ['cere'];  returnați json_encode ($ config); 

Luăm șirul JSON de intrare, decodificăm-l și dacă acesta conține un "necesita", vom folosi oare în fișierul nostru de configurare Satis, dar este posibil să dorim să oglindim toate versiunile unui pachet, nu doar ultima. Deci, poate că vrem să ne modificăm testul pentru a verifica dacă versiunea este" * "în Satis, indiferent de ce versiune exactă este composer.json.

funcționa testARequiredPackageInComposerWillBeInSatisAlso () $ real = $ this-> parseComposerConf ( ' "necesită":  "batjocorirea / batjocorirea": ​​"> = 0.7.2"'); $ this-> assertEquals ('*', json_decode ($ actual, true) ['necesita'] ['Dare de batjocură']); 

Acest lucru, evident, nu reușește cu un mesaj rece:

PHPUnit_Framework_ExpectationFailedException: Nu a reușit să se afirme că două șiruri sunt egale. Așteptat: * Actual:> = 0.7.2

Acum, trebuie să editați JSON-ul nostru înainte să îl re-codificăm.

funcția privată parseComposerConf ($ jsonConfig) $ addedConfig = json_decode ($ jsonConfig, true); $ config = json_decode (auto :: $ DEFAULT_CONFIG, true); $ config = $ acest-> addNewRequires ($ addConfig, $ config); returnează json_encode ($ config);  funcția privată toAllVersions ($ config) foreach ($ config ['require'] ca $ package => $ version) $ config ['require'] [$ package] = '*';  return $ config;  funcția privată addNewRequires ($ addedConfig, $ config) if (isset ($ addedConfig ['necesită'])) $ config ['require'] = $ addedConfig ['require']; $ config = $ this-> toAllVersiuni ($ config);  return $ config; 

Pentru a face testul, trebuie să repetăm ​​fiecare element al matricei de pachete necesare și să setăm versiunea lor la '*'. Consultați metoda toAllVersion () pentru mai multe detalii. Și pentru a grăbi puțin acest tutorial, am extras și câteva metode private în același pas. Pe aici, parseComoserConf () devine foarte descriptiv și ușor de înțeles. Am putea, de asemenea, în linie $ config în argumentele lui addNewRequires (), dar din motive estetice l-am lăsat pe două linii.

Dar ce zici de "necesită-dev"în composer.json?

Funcția testARquiredDevPackageInComposerWillBeInSatisAlso () $ real = $ this-> parseComposerConf ( „ "necesită-dev":  "batjocorirea / batjocorirea": ​​"> = 0.7.2", "PHPUnit / PHPUnit": "3.7.28" „); $ this-> assertEquals ('*', json_decode ($ actual, true) ['necesita'] ['Dare de batjocură']); $ this-> assertEquals ('*', json_decode ($ actual, true) ['cere'] ['phpunit / phpunit']); 

Acest lucru, evident, nu reușește. Putem face trecerea doar cu copierea / lipirea condiției noastre dacă addNewRequires ():

funcția privată addNewRequires ($ addedConfig, $ config) if (isset ($ addedConfig ['necesită'])) $ config ['require'] = $ addedConfig ['require']; $ config = $ this-> toAllVersiuni ($ config);  dacă (isset ($ addConfig ['require-dev'])) $ config ['cere'] = $ addedConfig ['require-dev']; $ config = $ this-> toAllVersiuni ($ config);  return $ config; 

Da, asta face trecerea, dar acele declarații duplicate dacă sunt urate. Să ne ocupăm de ele.

funcția privată addNewRequires ($ addedConfig, $ config) $ config = $ this-> addRequire ($ addconfig, 'require', $ config); $ config = $ acest-> addRequire ($ addconfig, 'require-dev', $ config); return $ config;  funcția privată addRequire ($ addConfig, $ string, $ config) if (isset ($ addedConfig [$ string])) $ $ config ['require'] = $ addedConfig [$ string]; $ config = $ this-> toAllVersiuni ($ config);  return $ config; 

Putem fi din nou fericiți, testele sunt verzi și ne-am reconfigurat codul. Cred că doar un test este lăsat să fie scris. Dacă am ambele "necesita" și "necesită-dev"în secțiunile composer.json?

testItCanParseComposerJsonWithBothSections funcție () $ real = $ this-> parseComposerConf ( „ "necesită":  "batjocorirea / batjocorirea": ​​"> = 0.7.2" "cere-dev":  "PHPUnit / PHPUnit":" 3.7.28 " '); $ this-> assertEquals ('*', json_decode ($ actual, true) ['necesita'] ['Dare de batjocură']); $ this-> assertEquals ('*', json_decode ($ actual, true) ['cere'] ['phpunit / phpunit']); 

Acest lucru nu reușește deoarece pachetele stabilite de "necesită-dev"va suprascrie pe cele ale"necesita"și vom avea o eroare:

Indice nedefinit: batjocură / batjocură

Doar adăugați un semn plus pentru a fuziona matricele și am terminat.

funcția privată addRequire ($ addConfig, $ string, $ config) if (isset ($ addedConfig [$ string])) $ $ config ['require'] + = $ addedConfig [$ string]; $ config = $ this-> toAllVersiuni ($ config);  return $ config; 

Testele trec. Logica noastră este terminată. Tot ce am lăsat este să extrageți metodele în propriul fișier și clasă. Versiunea finală a testelor și a testului SatisUpdater clasa poate fi găsită în codul sursă atașat.

Acum putem modifica scriptul nostru cron pentru a încărca parserul nostru și pentru al rula pe el composer.json. Acesta va fi specific pentru folderele speciale ale proiectelor dvs. Iată un exemplu pe care îl puteți adapta la sistemul dvs..

#! / Usr / local / bin / php parseComposerConf (file_get_contents ($ composerJsonFile)); file_put_contents ($ satisConf, $ conf); (sprintf ('/ path / to / satis / bin / satis construi% s% s', $ satisConf, $ outputDir), $ retval); ieșire ($ retval);

Efectuarea proiectului dvs. Utilizați oglinda

Am vorbit despre multe lucruri din acest articol, dar nu am menționat cum vom instrui proiectul nostru de a folosi oglinda în loc de Internet. Știi, implicit este packgist.org? Dacă nu facem așa ceva:

 "repositories": ["type": "compozitor", "url": "http: // your-mirror-server: 4680",

Acest lucru va face oglinda dvs. prima alegere pentru compozitor. Dar adăugând doar asta în composer.json din proiectul dvs. nu va dezactiva accesul la pachetul.org. Dacă un pachet nu poate fi găsit pe oglinda locală, acesta va fi descărcat de pe Internet. Dacă doriți să blocați această caracteristică, puteți dori, de asemenea, să adăugați următoarea linie în secțiunea de depozite de mai sus:

"ambalator": fals

Gândurile finale

Asta e. Oglindă locală, cu adaptarea și actualizarea automată a pachetelor. Colegii dvs. nu vor mai trebui să aștepte mult timp în timp ce aceștia sau serverul CI instalează toate cerințele proiectelor dvs. A se distra.

Cod