[{"data":1,"prerenderedAt":769},["ShallowReactive",2],{"/en-us/blog/tags/careers/":3,"navigation-fr-fr":19,"banner-fr-fr":440,"footer-fr-fr":453,"careers-tag-page-fr-fr":665},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"content":8,"config":10,"_id":12,"_type":13,"title":14,"_source":15,"_file":16,"_stem":17,"_extension":18},"/en-us/blog/tags/careers","tags",false,"",{"tag":9,"tagSlug":9},"careers",{"template":11},"BlogTag","content:en-us:blog:tags:careers.yml","yaml","Careers","content","en-us/blog/tags/careers.yml","en-us/blog/tags/careers","yml",{"_path":20,"_dir":21,"_draft":6,"_partial":6,"_locale":7,"data":22,"_id":436,"_type":13,"title":437,"_source":15,"_file":438,"_stem":439,"_extension":18},"/shared/fr-fr/main-navigation","fr-fr",{"logo":23,"freeTrial":28,"sales":33,"login":38,"items":43,"search":377,"minimal":413,"duo":427},{"config":24},{"href":25,"dataGaName":26,"dataGaLocation":27},"/fr-fr/","gitlab logo","header",{"text":29,"config":30},"Commencer un essai gratuit",{"href":31,"dataGaName":32,"dataGaLocation":27},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":34,"config":35},"Contacter l'équipe commerciale",{"href":36,"dataGaName":37,"dataGaLocation":27},"/fr-fr/sales/","sales",{"text":39,"config":40},"Connexion",{"href":41,"dataGaName":42,"dataGaLocation":27},"https://gitlab.com/users/sign_in/","sign in",[44,88,187,192,298,358],{"text":45,"config":46,"cards":48,"footer":71},"Plateforme",{"dataNavLevelOne":47},"platform",[49,55,63],{"title":45,"description":50,"link":51},"La plateforme DevSecOps alimentée par l'IA la plus complète",{"text":52,"config":53},"Découvrir notre plateforme",{"href":54,"dataGaName":47,"dataGaLocation":27},"/fr-fr/platform/",{"title":56,"description":57,"link":58},"GitLab Duo (IA)","Créez des logiciels plus rapidement en tirant parti de l'IA à chaque étape du développement",{"text":59,"config":60},"Découvrez GitLab Duo",{"href":61,"dataGaName":62,"dataGaLocation":27},"/fr-fr/gitlab-duo/","gitlab duo ai",{"title":64,"description":65,"link":66},"Choisir GitLab","10 raisons pour lesquelles les entreprises choisissent GitLab",{"text":67,"config":68},"En savoir plus",{"href":69,"dataGaName":70,"dataGaLocation":27},"/fr-fr/why-gitlab/","why gitlab",{"title":72,"items":73},"Démarrer avec",[74,79,84],{"text":75,"config":76},"Ingénierie de plateforme",{"href":77,"dataGaName":78,"dataGaLocation":27},"/fr-fr/solutions/platform-engineering/","platform engineering",{"text":80,"config":81},"Expérience développeur",{"href":82,"dataGaName":83,"dataGaLocation":27},"/fr-fr/developer-experience/","Developer experience",{"text":85,"config":86},"MLOps",{"href":87,"dataGaName":85,"dataGaLocation":27},"/fr-fr/topics/devops/the-role-of-ai-in-devops/",{"text":89,"left":90,"config":91,"link":93,"lists":97,"footer":169},"Produit",true,{"dataNavLevelOne":92},"solutions",{"text":94,"config":95},"Voir toutes les solutions",{"href":96,"dataGaName":92,"dataGaLocation":27},"/fr-fr/solutions/",[98,124,147],{"title":99,"description":100,"link":101,"items":106},"Automatisation","CI/CD et automatisation pour accélérer le déploiement",{"config":102},{"icon":103,"href":104,"dataGaName":105,"dataGaLocation":27},"AutomatedCodeAlt","/fr-fr/solutions/delivery-automation/","automated software delivery",[107,111,115,120],{"text":108,"config":109},"CI/CD",{"href":110,"dataGaLocation":27,"dataGaName":108},"/fr-fr/solutions/continuous-integration/",{"text":112,"config":113},"Développement assisté par l'IA",{"href":61,"dataGaLocation":27,"dataGaName":114},"AI assisted development",{"text":116,"config":117},"Gestion du code source",{"href":118,"dataGaLocation":27,"dataGaName":119},"/fr-fr/solutions/source-code-management/","Source Code Management",{"text":121,"config":122},"Livraison de logiciels automatisée",{"href":104,"dataGaLocation":27,"dataGaName":123},"Automated software delivery",{"title":125,"description":126,"link":127,"items":132},"Securité","Livrez du code plus rapidement sans compromettre la sécurité",{"config":128},{"href":129,"dataGaName":130,"dataGaLocation":27,"icon":131},"/fr-fr/solutions/security-compliance/","security and compliance","ShieldCheckLight",[133,138,143],{"text":134,"config":135},"Application Security Testing",{"href":136,"dataGaName":137,"dataGaLocation":27},"/solutions/application-security-testing/","Application security testing",{"text":139,"config":140},"Sécurité de la chaîne d'approvisionnement logicielle",{"href":141,"dataGaLocation":27,"dataGaName":142},"/fr-fr/solutions/supply-chain/","Software supply chain security",{"text":144,"config":145},"Software Compliance",{"href":146,"dataGaName":144,"dataGaLocation":27},"/solutions/software-compliance/",{"title":148,"link":149,"items":154},"Mesures",{"config":150},{"icon":151,"href":152,"dataGaName":153,"dataGaLocation":27},"DigitalTransformation","/fr-fr/solutions/visibility-measurement/","visibility and measurement",[155,159,164],{"text":156,"config":157},"Visibilité et mesures",{"href":152,"dataGaLocation":27,"dataGaName":158},"Visibility and Measurement",{"text":160,"config":161},"Gestion de la chaîne de valeur",{"href":162,"dataGaLocation":27,"dataGaName":163},"/fr-fr/solutions/value-stream-management/","Value Stream Management",{"text":165,"config":166},"Données d'analyse et informations clés",{"href":167,"dataGaLocation":27,"dataGaName":168},"/fr-fr/solutions/analytics-and-insights/","Analytics and insights",{"title":170,"items":171},"GitLab pour",[172,177,182],{"text":173,"config":174},"Entreprises",{"href":175,"dataGaLocation":27,"dataGaName":176},"/fr-fr/enterprise/","enterprise",{"text":178,"config":179},"PME",{"href":180,"dataGaLocation":27,"dataGaName":181},"/fr-fr/small-business/","small business",{"text":183,"config":184},"Secteur public",{"href":185,"dataGaLocation":27,"dataGaName":186},"/fr-fr/solutions/public-sector/","public sector",{"text":188,"config":189},"Tarifs",{"href":190,"dataGaName":191,"dataGaLocation":27,"dataNavLevelOne":191},"/fr-fr/pricing/","pricing",{"text":193,"config":194,"link":196,"lists":200,"feature":285},"Ressources",{"dataNavLevelOne":195},"resources",{"text":197,"config":198},"Afficher toutes les ressources",{"href":199,"dataGaName":195,"dataGaLocation":27},"/fr-fr/resources/",[201,234,257],{"title":202,"items":203},"Premiers pas",[204,209,214,219,224,229],{"text":205,"config":206},"Installation",{"href":207,"dataGaName":208,"dataGaLocation":27},"/fr-fr/install/","install",{"text":210,"config":211},"Guides de démarrage rapide",{"href":212,"dataGaName":213,"dataGaLocation":27},"/fr-fr/get-started/","quick setup checklists",{"text":215,"config":216},"Apprentissage",{"href":217,"dataGaLocation":27,"dataGaName":218},"https://university.gitlab.com/","learn",{"text":220,"config":221},"Documentation sur le produit",{"href":222,"dataGaName":223,"dataGaLocation":27},"https://docs.gitlab.com/","product documentation",{"text":225,"config":226},"Vidéos sur les bonnes pratiques",{"href":227,"dataGaName":228,"dataGaLocation":27},"/fr-fr/getting-started-videos/","best practice videos",{"text":230,"config":231},"Intégrations",{"href":232,"dataGaName":233,"dataGaLocation":27},"/fr-fr/integrations/","integrations",{"title":235,"items":236},"Découvrir",[237,242,247,252],{"text":238,"config":239},"Histoires de succès client",{"href":240,"dataGaName":241,"dataGaLocation":27},"/fr-fr/customers/","customer success stories",{"text":243,"config":244},"Blog",{"href":245,"dataGaName":246,"dataGaLocation":27},"/fr-fr/blog/","blog",{"text":248,"config":249},"Travail à distance",{"href":250,"dataGaName":251,"dataGaLocation":27},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":253,"config":254},"TeamOps",{"href":255,"dataGaName":256,"dataGaLocation":27},"/fr-fr/teamops/","teamops",{"title":258,"items":259},"Connecter",[260,265,270,275,280],{"text":261,"config":262},"Services GitLab",{"href":263,"dataGaName":264,"dataGaLocation":27},"/fr-fr/services/","services",{"text":266,"config":267},"Communauté",{"href":268,"dataGaName":269,"dataGaLocation":27},"/community/","community",{"text":271,"config":272},"Forum",{"href":273,"dataGaName":274,"dataGaLocation":27},"https://forum.gitlab.com/","forum",{"text":276,"config":277},"Événements",{"href":278,"dataGaName":279,"dataGaLocation":27},"/events/","events",{"text":281,"config":282},"Partenaires",{"href":283,"dataGaName":284,"dataGaLocation":27},"/fr-fr/partners/","partners",{"backgroundColor":286,"textColor":287,"text":288,"image":289,"link":293},"#2f2a6b","#fff","L'avenir du développement logiciel. Tendances et perspectives.",{"altText":290,"config":291},"carte promo The Source",{"src":292},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":294,"config":295},"Lire les articles les plus récents",{"href":296,"dataGaName":297,"dataGaLocation":27},"/fr-fr/the-source/","the source",{"text":299,"config":300,"lists":302},"Société",{"dataNavLevelOne":301},"company",[303],{"items":304},[305,310,316,318,323,328,333,338,343,348,353],{"text":306,"config":307},"À propos",{"href":308,"dataGaName":309,"dataGaLocation":27},"/fr-fr/company/","about",{"text":311,"config":312,"footerGa":315},"Emplois",{"href":313,"dataGaName":314,"dataGaLocation":27},"/jobs/","jobs",{"dataGaName":314},{"text":276,"config":317},{"href":278,"dataGaName":279,"dataGaLocation":27},{"text":319,"config":320},"Leadership",{"href":321,"dataGaName":322,"dataGaLocation":27},"/company/team/e-group/","leadership",{"text":324,"config":325},"Équipe",{"href":326,"dataGaName":327,"dataGaLocation":27},"/company/team/","team",{"text":329,"config":330},"Manuel",{"href":331,"dataGaName":332,"dataGaLocation":27},"https://handbook.gitlab.com/","handbook",{"text":334,"config":335},"Relations avec les investisseurs",{"href":336,"dataGaName":337,"dataGaLocation":27},"https://ir.gitlab.com/","investor relations",{"text":339,"config":340},"Centre de confiance",{"href":341,"dataGaName":342,"dataGaLocation":27},"/fr-fr/security/","trust center",{"text":344,"config":345},"Centre pour la transparence de l'IA",{"href":346,"dataGaName":347,"dataGaLocation":27},"/fr-fr/ai-transparency-center/","ai transparency center",{"text":349,"config":350},"Newsletter",{"href":351,"dataGaName":352,"dataGaLocation":27},"/company/contact/","newsletter",{"text":354,"config":355},"Presse",{"href":356,"dataGaName":357,"dataGaLocation":27},"/press/","press",{"text":359,"config":360,"lists":361},"Nous contacter",{"dataNavLevelOne":301},[362],{"items":363},[364,367,372],{"text":34,"config":365},{"href":36,"dataGaName":366,"dataGaLocation":27},"talk to sales",{"text":368,"config":369},"Aide",{"href":370,"dataGaName":371,"dataGaLocation":27},"/support/","get help",{"text":373,"config":374},"Portail clients GitLab",{"href":375,"dataGaName":376,"dataGaLocation":27},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":378,"login":379,"suggestions":386},"Fermer",{"text":380,"link":381},"Pour rechercher des dépôts et des projets, connectez-vous à",{"text":382,"config":383},"gitlab.com",{"href":41,"dataGaName":384,"dataGaLocation":385},"search login","search",{"text":387,"default":388},"Suggestions",[389,392,397,399,404,409],{"text":56,"config":390},{"href":61,"dataGaName":391,"dataGaLocation":385},"GitLab Duo (AI)",{"text":393,"config":394},"Suggestions de code (IA)",{"href":395,"dataGaName":396,"dataGaLocation":385},"/fr-fr/solutions/code-suggestions/","Code Suggestions (AI)",{"text":108,"config":398},{"href":110,"dataGaName":108,"dataGaLocation":385},{"text":400,"config":401},"GitLab sur AWS",{"href":402,"dataGaName":403,"dataGaLocation":385},"/fr-fr/partners/technology-partners/aws/","GitLab on AWS",{"text":405,"config":406},"GitLab sur Google Cloud ",{"href":407,"dataGaName":408,"dataGaLocation":385},"/fr-fr/partners/technology-partners/google-cloud-platform/","GitLab on Google Cloud",{"text":410,"config":411},"Pourquoi utiliser GitLab ?",{"href":69,"dataGaName":412,"dataGaLocation":385},"Why GitLab?",{"freeTrial":414,"mobileIcon":419,"desktopIcon":424},{"text":415,"config":416},"Commencer votre essai gratuit",{"href":417,"dataGaName":32,"dataGaLocation":418},"https://gitlab.com/-/trials/new/","nav",{"altText":420,"config":421},"Icône GitLab",{"src":422,"dataGaName":423,"dataGaLocation":418},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":420,"config":425},{"src":426,"dataGaName":423,"dataGaLocation":418},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"freeTrial":428,"mobileIcon":432,"desktopIcon":434},{"text":429,"config":430},"En savoir plus sur GitLab Duo",{"href":61,"dataGaName":431,"dataGaLocation":418},"gitlab duo",{"altText":420,"config":433},{"src":422,"dataGaName":423,"dataGaLocation":418},{"altText":420,"config":435},{"src":426,"dataGaName":423,"dataGaLocation":418},"content:shared:fr-fr:main-navigation.yml","Main Navigation","shared/fr-fr/main-navigation.yml","shared/fr-fr/main-navigation",{"_path":441,"_dir":21,"_draft":6,"_partial":6,"_locale":7,"title":442,"titleMobile":442,"button":443,"config":448,"_id":450,"_type":13,"_source":15,"_file":451,"_stem":452,"_extension":18},"/shared/fr-fr/banner","La plateforme GitLab Duo Agent est maintenant disponible en version bêta publique !",{"text":444,"config":445},"Essayer la version bêta",{"href":446,"dataGaName":447,"dataGaLocation":27},"/fr-fr/gitlab-duo/agent-platform/","duo banner",{"layout":449},"release","content:shared:fr-fr:banner.yml","shared/fr-fr/banner.yml","shared/fr-fr/banner",{"_path":454,"_dir":21,"_draft":6,"_partial":6,"_locale":7,"data":455,"_id":661,"_type":13,"title":662,"_source":15,"_file":663,"_stem":664,"_extension":18},"/shared/fr-fr/main-footer",{"text":456,"source":457,"edit":463,"contribute":468,"config":473,"items":478,"minimal":652},"Git est une marque déposée de Software Freedom Conservancy et notre utilisation de « GitLab » est sous licence",{"text":458,"config":459},"Afficher le code source de la page",{"href":460,"dataGaName":461,"dataGaLocation":462},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":464,"config":465},"Modifier cette page",{"href":466,"dataGaName":467,"dataGaLocation":462},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":469,"config":470},"Veuillez contribuer",{"href":471,"dataGaName":472,"dataGaLocation":462},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":474,"facebook":475,"youtube":476,"linkedin":477},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[479,502,556,589,623],{"title":45,"links":480,"subMenu":485},[481],{"text":482,"config":483},"Plateforme DevSecOps",{"href":54,"dataGaName":484,"dataGaLocation":462},"devsecops platform",[486],{"title":188,"links":487},[488,492,497],{"text":489,"config":490},"Voir les forfaits",{"href":190,"dataGaName":491,"dataGaLocation":462},"view plans",{"text":493,"config":494},"Pourquoi choisir GitLab Premium ?",{"href":495,"dataGaName":496,"dataGaLocation":462},"/fr-fr/pricing/premium/","why premium",{"text":498,"config":499},"Pourquoi choisir GitLab Ultimate ?",{"href":500,"dataGaName":501,"dataGaLocation":462},"/fr-fr/pricing/ultimate/","why ultimate",{"title":503,"links":504},"Solutions",[505,510,513,515,520,525,529,532,535,540,542,544,546,551],{"text":506,"config":507},"Transformation digitale",{"href":508,"dataGaName":509,"dataGaLocation":462},"/fr-fr/topics/digital-transformation/","digital transformation",{"text":511,"config":512},"Sécurité et conformité",{"href":136,"dataGaName":137,"dataGaLocation":462},{"text":121,"config":514},{"href":104,"dataGaName":105,"dataGaLocation":462},{"text":516,"config":517},"Développement agile",{"href":518,"dataGaName":519,"dataGaLocation":462},"/fr-fr/solutions/agile-delivery/","agile delivery",{"text":521,"config":522},"Transformation cloud",{"href":523,"dataGaName":524,"dataGaLocation":462},"/fr-fr/topics/cloud-native/","cloud transformation",{"text":526,"config":527},"SCM",{"href":118,"dataGaName":528,"dataGaLocation":462},"source code management",{"text":108,"config":530},{"href":110,"dataGaName":531,"dataGaLocation":462},"continuous integration & delivery",{"text":160,"config":533},{"href":162,"dataGaName":534,"dataGaLocation":462},"value stream management",{"text":536,"config":537},"GitOps",{"href":538,"dataGaName":539,"dataGaLocation":462},"/fr-fr/solutions/gitops/","gitops",{"text":173,"config":541},{"href":175,"dataGaName":176,"dataGaLocation":462},{"text":178,"config":543},{"href":180,"dataGaName":181,"dataGaLocation":462},{"text":183,"config":545},{"href":185,"dataGaName":186,"dataGaLocation":462},{"text":547,"config":548},"Formation",{"href":549,"dataGaName":550,"dataGaLocation":462},"/fr-fr/solutions/education/","education",{"text":552,"config":553},"Services financiers",{"href":554,"dataGaName":555,"dataGaLocation":462},"/fr-fr/solutions/finance/","financial services",{"title":193,"links":557},[558,560,562,564,567,569,573,575,577,579,581,583,585,587],{"text":205,"config":559},{"href":207,"dataGaName":208,"dataGaLocation":462},{"text":210,"config":561},{"href":212,"dataGaName":213,"dataGaLocation":462},{"text":215,"config":563},{"href":217,"dataGaName":218,"dataGaLocation":462},{"text":220,"config":565},{"href":222,"dataGaName":566,"dataGaLocation":462},"docs",{"text":243,"config":568},{"href":245,"dataGaName":246},{"text":570,"config":571},"Histoires de réussite client",{"href":572,"dataGaLocation":462},"/customers/",{"text":238,"config":574},{"href":240,"dataGaName":241,"dataGaLocation":462},{"text":248,"config":576},{"href":250,"dataGaName":251,"dataGaLocation":462},{"text":261,"config":578},{"href":263,"dataGaName":264,"dataGaLocation":462},{"text":253,"config":580},{"href":255,"dataGaName":256,"dataGaLocation":462},{"text":266,"config":582},{"href":268,"dataGaName":269,"dataGaLocation":462},{"text":271,"config":584},{"href":273,"dataGaName":274,"dataGaLocation":462},{"text":276,"config":586},{"href":278,"dataGaName":279,"dataGaLocation":462},{"text":281,"config":588},{"href":283,"dataGaName":284,"dataGaLocation":462},{"title":299,"links":590},[591,593,595,597,599,601,603,607,612,614,616,618],{"text":306,"config":592},{"href":308,"dataGaName":301,"dataGaLocation":462},{"text":311,"config":594},{"href":313,"dataGaName":314,"dataGaLocation":462},{"text":319,"config":596},{"href":321,"dataGaName":322,"dataGaLocation":462},{"text":324,"config":598},{"href":326,"dataGaName":327,"dataGaLocation":462},{"text":329,"config":600},{"href":331,"dataGaName":332,"dataGaLocation":462},{"text":334,"config":602},{"href":336,"dataGaName":337,"dataGaLocation":462},{"text":604,"config":605},"Sustainability",{"href":606,"dataGaName":604,"dataGaLocation":462},"/sustainability/",{"text":608,"config":609},"Diversité, inclusion et appartenance (DIB)",{"href":610,"dataGaName":611,"dataGaLocation":462},"/fr-fr/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":339,"config":613},{"href":341,"dataGaName":342,"dataGaLocation":462},{"text":349,"config":615},{"href":351,"dataGaName":352,"dataGaLocation":462},{"text":354,"config":617},{"href":356,"dataGaName":357,"dataGaLocation":462},{"text":619,"config":620},"Déclaration de transparence sur l'esclavage moderne",{"href":621,"dataGaName":622,"dataGaLocation":462},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":359,"links":624},[625,628,630,632,637,642,647],{"text":626,"config":627},"Échanger avec un expert",{"href":36,"dataGaName":37,"dataGaLocation":462},{"text":368,"config":629},{"href":370,"dataGaName":371,"dataGaLocation":462},{"text":373,"config":631},{"href":375,"dataGaName":376,"dataGaLocation":462},{"text":633,"config":634},"Statut",{"href":635,"dataGaName":636,"dataGaLocation":462},"https://status.gitlab.com/","status",{"text":638,"config":639},"Conditions d'utilisation",{"href":640,"dataGaName":641},"/terms/","terms of use",{"text":643,"config":644},"Déclaration de confidentialité",{"href":645,"dataGaName":646,"dataGaLocation":462},"/fr-fr/privacy/","privacy statement",{"text":648,"config":649},"Préférences en matière de cookies",{"dataGaName":650,"dataGaLocation":462,"id":651,"isOneTrustButton":90},"cookie preferences","ot-sdk-btn",{"items":653},[654,656,659],{"text":638,"config":655},{"href":640,"dataGaName":641,"dataGaLocation":462},{"text":657,"config":658},"Politique de confidentialité",{"href":645,"dataGaName":646,"dataGaLocation":462},{"text":648,"config":660},{"dataGaName":650,"dataGaLocation":462,"id":651,"isOneTrustButton":90},"content:shared:fr-fr:main-footer.yml","Main Footer","shared/fr-fr/main-footer.yml","shared/fr-fr/main-footer",{"allPosts":666,"featuredPost":745,"totalPagesCount":767,"initialPosts":768},[667,696,722],{"_path":668,"_dir":246,"_draft":6,"_partial":6,"_locale":7,"seo":669,"content":677,"config":689,"_id":692,"_type":13,"title":693,"_source":15,"_file":694,"_stem":695,"_extension":18},"/fr-fr/blog/five-fast-facts-about-docs-as-code-at-gitlab",{"title":670,"description":671,"ogTitle":670,"ogDescription":671,"noIndex":6,"ogImage":672,"ogUrl":673,"ogSiteName":674,"ogType":675,"canonicalUrls":673,"schema":676},"Documentation as code chez GitLab : 5 choses à savoir","Découvrez dans cet article comment nous utilisons la méthodologie « documentation as code » avec GitLab pour la rédaction de notre documentation technique.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749660257/Blog/Hero%20Images/pen.jpg","https://about.gitlab.com/blog/five-fast-facts-about-docs-as-code-at-gitlab","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Documentation as code chez GitLab : 5 choses à savoir\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Suzanne Selhorn\"},{\"@type\":\"Person\",\"name\":\"Susan Tacker\"},{\"@type\":\"Person\",\"name\":\"Diana Logan\"}],\n        \"datePublished\": \"2022-10-12\",\n      }",{"title":670,"description":671,"authors":678,"heroImage":672,"date":682,"body":683,"category":684,"tags":685,"updatedDate":688},[679,680,681],"Suzanne Selhorn","Susan Tacker","Diana Logan","2022-10-12","Chez GitLab, nous utilisons notre plateforme pour documenter notre produit. Notre équipe de rédaction technique utilise GitLab pour planifier, créer, réviser, éditer et publier le contenu que vous pouvez consulter au sein de la [documentation de GitLab](https://docs.gitlab.com/ \"Documentation de GitLab\"). En utilisant le workflow « doc as code », nos rédacteurs sont alors capables de produire une grande quantité de contenus.  \n\nSi vous n'êtes pas encore familier avec le concept de « documentation as code », en voici une définition rapide : \n\nLa « doc as code » consiste à publier de la documentation technique en utilisant les mêmes outils et processus qu'en développement de logiciel. La documentation se place au sein du même dépôt que le code, pour permettre un contrôle des versions.\n\nAvant d’adopter la méthodologie « documentation as code », découvrez cinq points intéressants sur la façon dont notre équipe procède. \n\n## Planifier les mises à jour des fonctionnalités et du contenu de notre documentation\n\nNos chefs de produit, UX Designers, ingénieurs et équipes d'assurance qualité planifient ensemble les fonctionnalités de GitLab. Lorsque vous planifiez une nouvelle version, vous utilisez probablement un tableau Kanban, ou créez un ticket avec un outil tiers.\n\nChez GitLab, nous utilisons des [epics](https://docs.gitlab.com/ee/user/group/epics/ \"Epics\") et un système de tickets pour planifier notre travail, ainsi que des [tableaux de bord](https://docs.gitlab.com/ee/user/project/issue_board.html) pour suivre notre progression. La transparence étant primordiale, nous rendons accessible à tous l’ensemble des informations et des discussions pour que l'équipe de rédaction technique puisse disposer d'une visibilité complète sur l'avancement des projets.\n\n![Planning de travail chez GitLab](https://about.gitlab.com/images/blogimages/planning_issue.png)\n\nDans le cas d'un projet de documentation de plus grande ampleur, nous effectuons l’ensemble de nos tâches depuis GitLab : suivi, modifications et résolution des tickets. De cette manière, vous pouvez visualiser l’ensemble du projet depuis un seul et même endroit et retrouver l’historique des modifications, à tout moment.\n\n## Échanger avec les équipes sur la documentation\n\nSi vous rédigez des articles régulièrement, vous savez à quel point il peut être difficile de faire relire votre contenu par une tierce personne.\n\nChez GitLab, nos développeurs sont les premiers à rédiger la description des nouvelles fonctionnalités, qu’ils enregistrent dans le même dépôt que leur code. Documenter les fonctionnalités fait partie de notre definition of done (DoD) – nos critères d'accomplissement des tâches. \n\nEnsuite, nos rédacteurs techniques révisent les contenus, y ajoutent des suggestions et les renvoient aux auteurs. Ils peuvent notamment effectuer des merge requests pour apporter des modifications. \n\nQue la demande vienne d'un rédacteur, développeur, ingénieur, ou tout autre contributeur de la communauté, nous avons tous la possibilité de commenter facilement le travail des autres. Pour ce faire, il suffit de sélectionner le bouton « Suggestion » et d'ajouter un commentaire. Vous pouvez apporter des modifications, et l'auteur de la merge request pourra les valider ou proposer une alternative. Vous pouvez également inviter d'autres personnes à participer en mentionnant leur nom d'utilisateur. C'est transparent et participatif.\n\n![Faire une suggestion en doc as code](https://about.gitlab.com/images/blogimages/suggestion.png)\n\nComme la documentation est en Markdown, un langage similaire à du texte brut, il est facile de visualiser les différences entre les versions des fichiers et de voir qui a apporté telle ou telle modification. \n\nAvec la «doc as code » , vous gagnez en efficacité et dites adieu aux versions obsolètes et aux commentaires malencontreusement effacés par une mise à jour. Si quelqu'un veut connaître les raisons d'une modification, il lui suffit de consulter l'historique de la page pour voir qui en est l'auteur, ligne par ligne.\n\n![Commentaires en doc as code](https://about.gitlab.com/images/blogimages/blame.png)\n\nPlus besoin de sauvegarder les versions d'un document PDF pour trouver l'origine d'une modification. Tout est dans GitLab.\n\n## Prévisualiser le contenu des documents\n\nChez GitLab, nos outils peuvent générer en local le contenu du site de notre documentation, mais vous pouvez aussi le visualiser directement depuis une merge request. Pour cela, il vous suffit d’ouvrir une merge request et de sélectionner «Voir l’application » pour tester et partager une idée. Le site de documentation modifié est dès lors consultable depuis une URL publique.\n\n![Prévisualisation d'une modification avec la fonction « Voir l'application »](https://about.gitlab.com/images/blogimages/view_app.png)\n\nVos modifications sont visibles, vous pouvez les modifier ou les valider telles quelles. Ce qui nous amène à une autre fonctionnalité utile de GitLab.\n\n## Tester chaque modification de contenu\n\nPeut-être utilisez-vous un outil tiers pour tester les liens de vos documents ou vérifier les règles de grammaire et d'orthographe. Sachez que certains de ces outils peuvent être intégrés dans GitLab et dans le workflow de rédaction.\n\nChaque rédacteur dispose de nos outils installés localement, pour contrôler le document à tous les niveaux sur sa machine locale. Pour les contributeurs qui ne disposent pas de ces outils, nous exécutons pour chaque validation une version une version de nos tests dans un pipeline.\n\n![Erreur Lint dans le code](https://about.gitlab.com/images/blogimages/lint_error_2.png)\n\nSi vous êtes un développeur sans grande expérience en rédaction, vous pourriez constater une erreur lors de la merge request à cause d'une faute de grammaire ou d’un non-respect de la charte éditoriale. Nous avons défini un ensemble de règles avec différents niveaux d'importance et effectuons des tests afin que nos contenus soient alignés avec notre [guide de style](https://docs.gitlab.com/ee/development/documentation/styleguide/ \"Guide de style de GitLab\") et notre glossaire.\n\n## Générer le contenu HTML hébergé sur GitLab Pages\n\nNotre [pipeline CI/CD](https://about.gitlab.com/fr-fr/topics/ci-cd/cicd-pipeline/ \"Qu'est-ce qu'un pipeline CI/CD ? \") convertit et compile notre contenu Markdown en HTML. Nous hébergeons ensuite le contenu sur GitLab Pages, sur le site [docs.gitlab.com](https://docs.gitlab.com/ \"Documentation de GitLab\").\n\n![Pipeline de déploiement de contenu](https://about.gitlab.com/images/blogimages/pipeline2.png)\n\nCette approche nous permet de mettre à jour notre documentation très régulièrement, voire même toutes les heures. Le contenu de notre documentation est donc toujours pertinent, et parfois même en avance sur la sortie de nouvelles fonctionnalités. Une situation somme toute naturelle puisque nous partageons en toute transparence notre planning de développement.\n\nComme vous avez pu le constater tout au long de cet article, nous apprécions vraiment travailler en doc as code. Passer à un seul outil pour gérer votre documentation peut certes demander un temps d'adaptation, mais GitLab prend en charge l'ensemble du processus de rédaction, pour toutes les personnes impliquées dans la rédaction des contenus. Et notre expertise s'appuie sur des années de pratique.\n\nConsultez cette vidéo pour en savoir plus sur le travail de rédaction en documentation as code chez GitLab :\n\n\u003C!-- blank line -->\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/ZlabtdA-gZE\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\u003C!-- blank line -->\n\nVous souhaitez contribuer à notre documentation open source ? Découvrez [toutes nos instructions](https://docs.gitlab.com/ee/development/documentation/workflow.html#how-to-update-the-docs \"Instructions pour contribuer à la documentation de GitLab\").\n","insights",[9,686,687],"contributors","inside GitLab","2024-08-08",{"slug":690,"featured":6,"template":691},"five-fast-facts-about-docs-as-code-at-gitlab","BlogPost","content:fr-fr:blog:five-fast-facts-about-docs-as-code-at-gitlab.yml","Five Fast Facts About Docs As Code At Gitlab","fr-fr/blog/five-fast-facts-about-docs-as-code-at-gitlab.yml","fr-fr/blog/five-fast-facts-about-docs-as-code-at-gitlab",{"_path":697,"_dir":246,"_draft":6,"_partial":6,"_locale":7,"seo":698,"content":704,"config":716,"_id":718,"_type":13,"title":719,"_source":15,"_file":720,"_stem":721,"_extension":18},"/fr-fr/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions",{"title":699,"description":700,"ogTitle":699,"ogDescription":700,"noIndex":6,"ogImage":701,"ogUrl":702,"ogSiteName":674,"ogType":675,"canonicalUrls":702,"schema":703},"Programmation en Rust à l’aide de l'IA : tutoriel","Poursuivez votre apprentissage de la programmation en Rust à l'aide de ce tutoriel et des suggestions de code alimentées par l'IA de GitLab Duo.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749662439/Blog/Hero%20Images/codewithheart.png","https://about.gitlab.com/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Programmation en Rust à l’aide de l'IA : tutoriel\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Michael Friedrich\"}],\n        \"datePublished\": \"2023-10-12\",\n      }",{"title":699,"description":700,"authors":705,"heroImage":701,"date":707,"body":708,"category":709,"tags":710,"updatedDate":715},[706],"Michael Friedrich","2023-10-12","Il y a 20 ans, pour apprendre un nouveau langage de programmation, il fallait installer la bibliothèque MSDN de Visual Studio 6 avec 6 CD-ROM. Trouver les bons algorithmes prenait du temps et nécessitait des livres et des recherches manuelles. Aujourd'hui, grâce à la collaboration à distance et à l'intelligence artificielle (IA), vous pouvez facilement créer un [espace de développement à distance](https://about.gitlab.com/blog/quick-start-guide-for-gitlab-workspaces/), partager votre écran et coder en groupe. De plus, [GitLab Duo offre des suggestions de code](/gitlab-duo/) alimentées par l'IA, adaptées à votre style de programmation et à votre expérience, même avec peu d'informations et de contexte.\n\nCe tutoriel basé sur notre [article d'introduction à Rust](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/) vous guidera dans le développement d'une application simple de lecteur de flux.\n\n## Préparations\nConfigurez [VS Code](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/#vs-code) et [votre environnement de développement Rust](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/#development-environment-for-rust).\n\n### Suggestions de code\nVérifiez les suggestions de code avant de les accepter. GitLab Duo génère des suggestions de code en temps réel que vous pouvez accepter en appuyant sur la touche `tab`. Notez qu'il est plus fiable d'écrire du nouveau code que de refactoriser un code existant. Notez aussi que l'IA peut proposer des suggestions différentes pour le même code. Tenez compte des [limitations connues](https://docs.gitlab.com/ee/user/project/repository/code_suggestions.html#known-limitations) qui peuvent affecter votre apprentissage.\n\n**Astuce :** les suggestions de code prennent en charge les instructions multilignes. Affinez les spécifications si besoin pour obtenir de meilleures suggestions.\n\n```rust\n    // Create a function that iterates over the source array\n    // and fetches the data using HTTP from the RSS feed items.\n    // Store the results in a new hash map.\n    // Print the hash map to the terminal.\n```\n\nLe volet de l'extension VS Code s'affiche lorsqu'une suggestion est proposée. Utilisez `tab` pour accepter les lignes suggérées, ou `cmd cursor right` pour accepter un mot. Le menu à trois points vous permet d'afficher la barre d'outils à tout moment.\n\n![Volet de l'extension VS Code des suggestions de code de GitLab Duo avec des instructions](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_code_suggestions_options_overlay_keep_toolbar.png){: .shadow}\n\n## Ressources Rust\nRust est l'un des [langages pris en charge par les suggestions de code](https://docs.gitlab.com/ee/user/project/repository/code_suggestions.html#supported-languages). Le tutoriel [« Rust by Example »](https://doc.rust-lang.org/rust-by-example/) et le [livre officiel « Le langage de programmation Rust »](https://doc.rust-lang.org/book/) sont une bonne aide pour les débutants. Nous les utiliserons également dans cet article.\n\n### Création d'une application de lecture de flux\nIl existe plusieurs façons d'apprendre Rust en créant une application. Vous pouvez utiliser des bibliothèques Rust, appelées `Crates`. Nous les explorerons pus tard dans ce tutoriel. Vous pouvez aussi créer une application pour traiter des fichiers image en ligne de commande, résoudre un labyrinthe ou des Sudokus, ou même développer des jeux. Le livre [Hands-on Rust](https://hands-on-rust.com/) apprend Rust par la création d'un jeu d'exploration de donjon. Ma collègue, Fatima Sarah Khalid, a conçu [Dragon Realm en C++ en s'aidant de l'IA](/blog/building-a-text-adventure-using-cplusplus-and-code-suggestions/).\n\nNous allons construire une application pour collecter des informations depuis des versions de sécurité, des articles de blog et des forums comme Hacker News, via des flux RSS. Nous filtrerons certains mots-clés ou des versions spécifiques dans ces flux. Voici ce que notre application devra faire :\n\n1. Récupérer des données depuis des sites web HTTP, API REST, mais surtout depuis des flux RSS.\n2. Analyser les données.\n3. Présenter les données à l'utilisateur ou les sauvegarder.\n4. Optimiser la lecture des flux.\n\nLes données fournies par l'application dans notre exemple sont présentées dans cet article, après les étapes d'apprentissage :\n\n![Terminal VS Code, exécution de Cargo avec des sorties d'éléments formatées](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_cargo_run_formatted_output_final.png)\n\nL'application doit être modulaire, servant de base pour ajouter d'autres données, filtres et hooks pour des actions ultérieures.\n\n### Initialisation du projet\nRappel : `cargo init` dans la racine du projet crée la structure du fichier, y compris le point d'entrée `main()`. Nous allons créer et utiliser des modules Rust à l'étape suivante.\n\nD'abord, créez un répertoire nommé `learn-rust-ai-app-reader`, ouvrez-le et exécutez `cargo init`, ce qui initialise également (`git init`) un dépôt Git local. Configurez ensuite le dépôt distant avec l'URL par exemple `https://gitlab.com/gitlab-de/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader`, ajustez le chemin de votre espace de nommage, et effectuez un push pour [créer automatiquement un nouveau projet privé sur GitLab](https://docs.gitlab.com/ee/user/project/#create-a-new-project-with-git-push).\n\n```shell\nmkdir learn-rust-ai-app-reader\ncd learn-rust-ai-app-reader\n\ncargo init\n\ngit remote add origin https://gitlab.com/gitlab-de/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader.git\ngit push --set-upstream origin main\n```\n\nOuvrez VS Code à partir du répertoire créé. La CLI `code` ouvre une fenêtre VS Code sur macOS.\n\n```shell\ncode .\n```\n\n### Définition des URL des flux RSS\nAjoutez une table de hachage pour stocker les URL des flux RSS dans le fichier `src/main.rs` dans la fonction `main()`. Utilisez les suggestions de code de GitLab Duo pour créer un objet [`HashMap`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html) avec un commentaire multilignes et initialisez-le avec les valeurs par défaut pour Hacker News et TechCrunch. Vérifiez l'exactitude des URL incluses dans les suggestions.\n\n```rust\nfn main() {\n    // Define RSS feed URLs in the variable rss_feeds\n    // Use a HashMap\n    // Add Hacker News and TechCrunch\n    // Ensure to use String as type\n\n}\n```\n\nLe commentaire intégré au code doit inclure :\n\n1. Le nom de la variable `rss_feeds`.\n2. Le type `HashMap`.\n3. Les paires clé/valeur initiales.\n4. La string comme type utilisable avec les appels `to_string()`).\n\nVoici une suggestion possible :\n\n```rust\nuse std::collections::HashMap;\n\nfn main() {##$_0A$##    // Define RSS feed URLs in the variable rss_feeds##$_0A$##    // Use a HashMap##$_0A$##    // Add Hacker News and TechCrunch##$_0A$##    // Ensure to use String as type##$_0A$##    let rss_feeds = HashMap::from([##$_0A$##        (\"Hacker News\".to_string(), \"https://news.ycombinator.com/rss\".to_string()),##$_0A$##        (\"TechCrunch\".to_string(), \"https://techcrunch.com/feed/\".to_string()),##$_0A$##    ]);##$_0A$####$_0A$##}\n```\n\n![VS Code avec les suggestions de code pour les URL des flux RSS pour Hacker News et TechCrunch](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_main_array_rss_feed_urls_suggested.png)\n\nOuvrez un terminal dans VS Code (cmd+maj+p, recherchez `terminal`), exécutez `cargo build` pour appliquer les modifications. Ajoutez la ligne d'importation `use std::collections::HashMap;` suite au message d'erreur.\n\nEnsuite, utilisez les URL des flux RSS. Dans notre [article](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/) précédent, nous avons expliqué la division du code en fonctions. Ce tutoriel se concentre sur l'organisation modulaire du code avec des modules Rust.\n\n## Modules\nLes [modules](https://doc.rust-lang.org/rust-by-example/mod.html) organisent le code et peuvent restreindre l'accès aux fonctions à partir de la portée main(). Pour notre application, nous récupérerons le flux RSS et analyserons la réponse XML. La structure appelante `main()` n'accèdera qu'à la fonction `get_feeds()`. Les autres fonctionnalités sont disponibles dans le module.\n\nCréez un fichier `feed_reader.rs` dans le répertoire `src/`. Demandez aux suggestions de code de créer un module public nommé `feed_reader` avec une fonction publique `get_feeds()` prenant une string HashMap en entrée. Assurez-vous que le nom du fichier et du module correspondent et suivent la [structure de module Rust](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html).\n\n![Suggestions de code : créez un module public, avec des types de fonctions et d'intrants](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_public_module_function_input.png){: .shadow}\n\nEn fournissant aux suggestions de code le nom de la variable et son type, le module `std::collections::HashMap` sera automatiquement importé. Astuce : ajustez les commentaires et les types de variables pour améliorer les suggestions. Passer des paramètres de fonction sous forme de références d'objet est une bonne pratique en Rust.\n\n```rust\n// Create public module feed_reader\n// Define get_feeds() function which takes rss_feeds as String HashMap reference as input\npub mod feed_reader {\n    use std::collections::HashMap;\n\n    pub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n        // Do something with the RSS feeds\n    }\n}\n```\n\n![Suggestions de code : module public avec la fonction `get_feeds()` et la variable d'intrant suggérée](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_public_module_function_input.png){: .shadow}\n\nPour guider les suggestions de code, suivez ces étapes :\n\n1. `// Iterate over the RSS feed URLs`\n2. `// Fetch URL content`\n3. `// Parse XML body`\n4. `// Print the result`\n\n![Suggestions de code : module public avec la fonction `get_feeds()`, étape 1 : itération](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_01_iterate.png){: .shadow}\n\n![Suggestions de code : module public avec la fonction `get_feeds()`, étape 2 : récupération du contenu de l'URL](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_02_fetch_content.png){: .shadow}\n\n![Suggestions de code : module public avec la fonction `get_feeds()`, étape 3 : analyse du corps XML](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_03_parse_body.png){: .shadow}\n\n![Suggestions de code : module public avec la fonction `get_feeds()`, étape 4 : impression des résultats](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_04_print_result.png){: .shadow}\n\nSuggestion de code possible :\n\n```rust\n// Create public module feed_reader\n// Define get_feeds() function which takes rss_feeds as String HashMap reference as input\npub mod feed_reader {\n    use std::collections::HashMap;\n\n    pub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n        // Iterate over the RSS feed URLs\n        for (name, url) in rss_feeds {\n            println!(\"{}: {}\", name, url);\n\n            // Fetch URL content\n            let body = reqwest::blocking::get(url).unwrap().text().unwrap();\n\n            // Parse XML body\n            let parsed_body = roxmltree::Document::parse(&body).unwrap();\n\n            // Print the result\n            println!(\"{:#?}\", parsed_body);\n        }\n    }\n}\n```\n\nRemarquez le nouveau mot-clé : [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html). Rust n'accepte pas les valeurs `null` et utilise le [type `Option`](https://doc.rust-lang.org/rust-by-example/std/option.html) pour représenter les valeurs. Si vous avez la certitude d'utiliser un type encapsulé comme `Text` ou `String`, vous pouvez appeler la méthode `unwrap()` pour obtenir la valeur, mais elle provoquera un arrêt brutal du programme si la valeur est `None`.\n\n**Remarque** : les suggestions de code se réfèrent à la fonction `reqwest::blocking::get` pour suivre l'instruction `// Fetch URL content` du commentaire. Le nom de la [crate `reqwest`](https://docs.rs/reqwest/latest/reqwest/) n'est pas une faute de frappe. Cette crate simplifie les requêtes HTTP, en mode asynchrone ou bloquant.\n\nL'analyse XML peut varier selon les flux RSS, alors testez la fonction `get_feeds()` et ajustez le code si nécessaire.\n\n### Appel de la fonction du module dans main()\n\nLa fonction main() ne connaît pas encore la fonction `get_feeds()`. Nous devons donc importer son module. Contrairement à d'autres langages de programmation, qui utilisent les mots-clés `include` ou `import`, Rust utilise des répertoires de fichiers\n\npour organiser les modules. Dans notre exemple, les deux fichiers source existent au même niveau de répertoire. `feed_reader.rs` est interprété comme une crate, contenant un module appelé `feed_reader`, qui définit la fonction `get_feeds()`.\n\n```\nsrc/\n  main.rs\n  feed_reader.rs\n```\n\nPour accéder à `get_feeds()` à partir du fichier `feed_reader.rs`, commencez par [indiquer le chemin du module](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html) dans la portée `main.rs`, puis appeler le chemin complet de la fonction.\n\n```rust\nmod feed_reader;\n\nfn main() {\n\n    feed_reader::feed_reader::get_feeds(&rss_feeds);\n\n```\n\nUtilisez le mot-clé `use` pour importer le chemin complet de la fonction avec un nom court.\n\n```rust\nmod feed_reader;\nuse feed_reader::feed_reader::get_feeds;\n\nfn main() {\n\n    get_feeds(&rss_feeds);\n\n```\n\n**Astuce :** consultez [l'article « Clear explanation of the Rust module system »](https://www.sheshbabu.com/posts/rust-module-system/) pour plus de détails.\n\n```diff\n\nfn main() {\n    // ...\n\n    // Print feed_reader get_feeds() output\n    println!(\"{}\", feed_reader::get_feeds(&rss_feeds));\n```\n\n```rust\nuse std::collections::HashMap;\n\nmod feed_reader;\n// Alternative: Import full function path\n//use feed_reader::feed_reader::get_feeds;\n\nfn main() {\n    // Define RSS feed URLs in the variable rss_feeds\n    // Use a HashMap\n    // Add Hacker News and TechCrunch\n    // Ensure to use String as type\n    let rss_feeds = HashMap::from([\n        (\"Hacker News\".to_string(), \"https://news.ycombinator.com/rss\".to_string()),\n        (\"TechCrunch\".to_string(), \"https://techcrunch.com/feed/\".to_string()),\n    ]);\n\n    // Call get_feeds() from feed_reader module\n    feed_reader::feed_reader::get_feeds(&rss_feeds);\n    // Alternative: Imported full path, use short path here.\n    //get_feeds(&rss_feeds);\n}\n```\n\nExécutez `cargo build` à nouveau dans le terminal pour compiler le code.\n\n```shell\ncargo build\n```\n\nDemander des suggestions de code pour des bibliothèques courantes (comme les requêtes HTTP ou l'analyse XML) peut provoquer des erreurs de compilation telles que :\n\n1. Erreur : `could not find blocking in reqwest`. Solution : activez la fonctionnalité `blocking` pour la crate dans `Config.toml`: `reqwest = { version = \"0.11.20\", features = [\"blocking\"] }`.\n2. Erreur : `failed to resolve: use of undeclared crate or module reqwest`. Solution : ajoutez la crate `reqwest`.\n3. Erreur : `failed to resolve: use of undeclared crate or module roxmltree`. Solution : ajoutez la crate `roxmltree`.\n\n```shell\nvim Config.toml\n\nreqwest = { version = \"0.11.20\", features = [\"blocking\"] }\n```\n\n```shell\ncargo add reqwest\ncargo add roxmltree\n```\n\n**Astuce :** copiez le message d'erreur avec l'en-tête `Rust \u003Cerror message>` dans un navigateur pour vérifier si la crate manquante est disponible. Cette recherche vous dirigera sur crates.io. Ajoutez ensuite les dépendances manquantes.\n\nAprès la compilation, exécutez le code avec `cargo run` et vérifiez la sortie du flux RSS de Hacker News.\n\n![Terminal VS Code, exécution de Cargo pour récupérer le flux XML de Hacker News](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_fetch_rss_feed_output_hacker_news.png){: .shadow}\n\nProchaine étape : analyse XML dans un format lisible par un humain Nous allons maintenant explorer les solutions existantes et le rôle des crates Rust.\n\n## Crates\nLes flux RSS suivent des protocoles et spécifications communs. Pour analyser les éléments XML et comprendre la structure de l'objet sous-jacent, vérifiez si un développeur a déjà résolu ce problème et utilisez son code.\n\nEn Rust, le code réutilisable est organisé en [`Crates`](https://doc.rust-lang.org/rust-by-example/crates.html) et disponible sur crates.io. Ajoutez ces dépendances dans le fichier `Config.toml` dans la section `[dependencies]` ou avec `cargo add \u003Cname>`.\n\nPour notre application, utilisez la [crate feed-rs](https://crates.io/crates/feed-rs) en exécutant la commande suivante dans le terminal :\n\n```shell\ncargo add feed-rs\n```\n\n![Terminal VS Code Terminal : ajout d'une crate, vérification dans le fichier Config.toml](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_rust_crate_add_feed-rs_explained.png)\n\n### feed-rs pour analyser un flux XML\nAccédez à `src/feed_reader.rs` pour modifier l'analyse XML. GitLab Duo sait appeler la fonction `parser::parse` de la crate `feed-rs`. Les suggestion de code comprennent le cas particulier : `feed-rs` [attend des octets bruts en entrée, et non des chaînes](https://docs.rs/feed-rs/latest/feed_rs/parser/fn.parse_with_uri.html) pour déterminer l'encodage. Fournissez des instructions dans le commentaire pour obtenir le résultat prévu.\n\n```rust\n            // Parse XML body with feed_rs parser, input in bytes\n            let parsed_body = feed_rs::parser::parse(body.as_bytes()).unwrap();\n```\n\n![Suggestions de code : module public avec la fonction `get_feeds()`, étape 5 : modification de l'analyseur XML en feed-rs](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_05_use_feed_rs_to_parse.png){: .shadow}\n\nLes avantages de `feed-rs` se révèlent en exécutant votre programme avec la commande `cargo run` : les clés et valeurs sont converties en types d'objets Rust réutilisables.\n\n![Terminal VS Code, exécution de Cargo pour récupérer le flux XML de Hacker News](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_fetch_rss_feed_output_hacker_news_feed_rs.png){: .shadow}\n\n## Configuration du runtime : arguments de programme\nJusqu'à présent, nous avons utilisé des valeurs de flux RSS codées en dur. La prochaine étape est de configurer ces flux RSS au moment de l'exécution.\n\nRust propose des [arguments de programme](https://doc.rust-lang.org/rust-by-example/std_misc/arg.html) via la bibliothèque « Std misc » standard. Il est plus rapide d'apprendre [l'analyse des arguments](https://doc.rust-lang.org/rust-by-example/std_misc/arg/matching.html) directement que d'utiliser des crates (par exemple, la crate [clap](https://docs.rs/clap/latest/clap/)) ou de passer par un fichier de configuration ou un autre format ([TOML](https://toml.io/en/), YAML). Pour cet article, j'ai bien essayé d'autres pistes, mais sans succès. Relevez le défi et essayez de configurer les flux RSS d'une autre manière.\n\nUne méthode simple consiste à passer les paramètres sous la forme `\"name,url\"` avec les valeurs séparées par une virgule `,` pour extraire le nom et l'URL. Demandez aux suggestions de code d'effectuer cette action et d'inclure les nouvelles valeurs dans la table de hachage `rss_feeds`. La variable peut ne pas être modifiable et doit être remplacée par `let mut rss_feeds`.\n\nAccédez à `src/main.rs` et ajoutez le code suivant à la fonction `main()` après la variable `rss_feeds`. Écrivez un commentaire qui définit les arguments de programme et vérifiez le code suggéré.\n\n```rust\n    // Program args, format \"name,url\"\n    // Split value by , into name, url and add to rss_feeds\n```\n\n![Suggestions de code pour les arguments de programme et découpage des paires nom,URL pour la variable rss_feeds](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_program_args_boring_solution.png){: .shadow}\n\nDans notre exemple, le code est le suivant :\n\n```rust\nfn main() {\n    // Define RSS feed URLs in the variable rss_feeds\n    // Use a HashMap\n    // Add Hacker News and TechCrunch\n    // Ensure to use String as type\n    let mut rss_feeds = HashMap::from([\n        (\"Hacker News\".to_string(), \"https://news.ycombinator.com/rss\".to_string()),\n        (\"TechCrunch\".to_string(), \"https://techcrunch.com/feed/\".to_string()),\n    ]);\n\n    // Program args, format \"name,url\"\n    // Split value by , into name, url and add to rss_feeds\n    for arg in std::env::args().skip(1) {\n        let mut split = arg.split(\",\");\n        let name = split.next().unwrap();\n        let url = split.next().unwrap();\n        rss_feeds.insert(name.to_string(), url.to_string());\n    }\n\n    // Call get_feeds() from feed_reader module\n    feed_reader::feed_reader::get_feeds(&rss_feeds);\n    // Alternative: Imported full path, use short path here.\n    //get_feeds(&rss_feeds);\n}\n```\n\nPassez les arguments directement avec `cargo run`, avant les arguments contenant `--`. Ajoutez des guillemets doubles à tous les arguments et une virgule après le nom. Utilisez l'URL du flux RSS comme argument. Séparez les arguments par des espaces.\n\n```\ncargo build\n\ncargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\n![Terminal VS Code, exemple de sortie de flux RSS pour le blog GitLab](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_gitlab_blog_rss_feed_example.png){: .shadow}\n\n### Gestion des erreurs des intrants saisis\nSi l'entrée ne correspond pas aux attentes du programme, [générez une erreur](https://doc.rust-lang.org/rust-by-example/error.html) pour aider à corriger les arguments, par exemple en cas d'URL invalide. Ajoutez un commentaire dans le code pour que les suggestions de code incluent une vérification de l'URL.\n\n```rust\n    // Ensure that URL contains a valid format, otherwise throw an error\n```\n\nVérifiez si `url` commence par `http://` ou `https://`. Sinon, utilisez la [macro panic!](https://doc.rust-lang.org/rust-by-example/std/panic.html) pour générer une erreur. Dans notre exemple, le code est le suivant :\n\n```rust\n    // Program args, format \"name,url\"\n    // Split value by , into name, url and add to rss_feeds\n    for arg in std::env::args().skip(1) {\n        let mut split = arg.split(\",\");\n        let name = split.next().unwrap();\n        let url = split.next().unwrap();\n\n        // Ensure that URL contains a valid format, otherwise throw an error\n        if !url.starts_with(\"http://\") && !url.starts_with(\"https://\") {\n            panic!(\"Invalid URL format: {}\", url);\n        }\n\n        rss_feeds.insert(name.to_string(), url.to_string());\n    }\n```\n\nTestez la gestion des erreurs en supprimant `:` dans l'une des chaînes d'URL. Ajoutez la variable d'environnement `RUST_BACKTRACE=full` pour un affichage plus détaillé des résultats lorsque l'appel `panic()`.\n\n```\nRUST_BACKTRACE=full cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https//www.cncf.io/feed/\"\n```\n\n![Terminal VS Code avec un format d'URL incorrect, traçage des erreurs suite à un arrêt brutal du programme](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_url_format_error_panic_backtrace.png){: .shadow}\n\n## Persistance et stockage des données\nUne solution classique consiste à stocker les données du flux analysé dans un nouveau fichier. Demandez aux suggestions de code de générer un nom de fichier incluant le nom du flux RSS et la date au format ISO.\n\n```rust\n    // Parse XML body with feed_rs parser, input in bytes\n    let parsed_body = feed_rs::parser::parse(body.as_bytes()).unwrap();\n\n    // Print the result\n    println!(\"{:#?}\", parsed_body);\n\n    // Dump the parsed body to a file, as name-current-iso-date.xml\n    let now = chrono::offset::Local::now();\n    let filename = format!(\"{}-{}.xml\", name, now.format(\"%Y-%m-%d\"));\n    let mut file = std::fs::File::create(filename).unwrap();\n    file.write_all(body.as_bytes()).unwrap();\n```\n\nUtilisez la [crate chrono](https://crates.io/crates/chrono). Ajoutez-la à l'aide de `cargo add chrono`, puis exécutez `cargo build` et `cargo run`.\n\nLes fichiers seront enregistrés dans le répertoire où `cargo run` est exécuté. Si vous exécutez le binaire directement dans le répertoire `target/debug/`, les fichiers y seront sauvegardés.\n\n![VS Code avec fichier de contenu du flux RSS CNCF, enregistré sur le disque](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_cncf_rss_feed_saved_on_disk.png)\n\n## Optimisation\nLes URL dans `rss_feeds` sont traitées de manière séquentielle, ce qui peut être lent avec plus de 100 URL. Pourquoi ne pas les traiter en parallèle ?\n\n### Exécution asynchrone\nRust permet l'exécution asynchrone avec des [threads](https://doc.rust-lang.org/book/ch16-01-threads.html).\n\nLa méthode la plus simple est de créer un thread pour chaque URL de flux RSS. Nous explorerons les stratégies d'optimisation ultérieurement. Avant de passer à l'exécution parallèle, mesurez le temps d'exécution séquentiel en faisant précéder `time` par `cargo run`.\n\n```\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n0.21s user 0.08s system 10% cpu 2.898 total\n```\n\nCet exercice peut nécessiter du codage manuel. Conservez l'état séquentiel dans une validation Git et une nouvelle branche `sequential-exec` pour comparer l'impact de l'exécution parallèle.\n\n```shell\ngit commit -avm \"Sequential execution working\"\ngit checkout -b sequential-exec\ngit push -u origin sequential-exec\n\ngit checkout main\n```\n\n### Création de threads\nOuvrez `src/feed_reader.rs` et réusinez la fonction `get_feeds()`. Lancez une validation Git de l'état actuel, puis supprimez le contenu de la portée de la fonction. Ajoutez les commentaires suivants dans le code comme instructions pour les suggestions de code :\n\n1. `// Store threads in vector` : stockez les gestionnaires de thread dans un vecteur pour attendre que tous les threads se terminent à la fin de l'appel de la fonction.\n2. `// Loop over rss_feeds and spawn threads` : créez un code standard pour itérer sur les flux RSS et générer un thread pour chacun.\n\nAjoutez les instructions `use` suivantes pour les modules `thread` et `time`.\n\n```rust\n    use std::thread;\n    use std::time::Duration;\n```\n\nContinuez à écrire le code et fermez la boucle for. Ajoutez les gestionnaires de thread au vecteur `threads` et joignez-les à la fin de la fonction.\n\n```rust\n    pub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n\n        // Store threads in vector\n        let mut threads: Vec\u003Cthread::JoinHandle\u003C()>> = Vec::new();\n\n        // Loop over rss_feeds and spawn threads\n        for (name, url) in rss_feeds {\n            let thread_name = name.clone();\n            let thread_url = url.clone();\n            let thread = thread::spawn(move || {\n\n            });\n            threads.push(thread);\n        }\n\n        // Join threads\n        for thread in threads {\n            thread.join().unwrap();\n        }\n    }\n```\n\nAjoutez la crate `thread`, compilez et exécutez le code.\n\n```shell\ncargo add thread\n\ncargo build\n\ncargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\nAucune donnée n'est traitée ni affichée. Explorez les nouveaux mots-clés avant de réintégrer la fonctionnalité.\n\n### Portées, threads et fermetures des fonctions\nLe code suggéré utilise de nouveaux mots-clés et design patterns. Le gestionnaire de thread de type `thread::JoinHandle` permet d'attendre la fin des threads ([join()](https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles)).\n\n`thread::spawn()` génère un thread pour passer un objet fonction. Une expression [de fermeture](https://doc.rust-lang.org/book/ch13-01-closures.html) est passée en tant que fonction anonyme. Les paramètres de fermeture sont passés avec la syntaxe `||`. La [fermeture `move`](https://doc.rust-lang.org/book/ch16-01-threads.html#using-move-closures-with-threads) déplace les variables de portée de la fonction dans celle du thread. Pas besoin de préciser manuellement les variables qui doivent être passées dans la nouvelle portée de la fonction/fermeture.\n\nCependant, `rss_feeds` est passé comme référence `&` par la structure d'appel de la fonction `get_feeds()` et n'est valide que dans la portée de la fonction. Utilisez le code suivant pour provoquer une erreur :\n\n```rust\npub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n\n    // Store threads in vector\n    let mut threads: Vec\u003Cthread::JoinHandle\u003C()>> = Vec::new();\n\n    // Loop over rss_feeds and spawn threads\n    for (key, value) in rss_feeds {\n        let thread = thread::spawn(move || {\n            println!(\"{}\", key);\n        });\n    }\n}\n```\n\n![Terminal VS Code, erreur liée à la portée de la variable et aux références avec une fermeture déplacée dans un thread](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_cargo_build_error_function_threads_variable_scopes.png){: .shadow}\n\nLa variable `key`, pourtant créée dans la portée de la fonction, fait référence à `rss_feeds` et ne peut pas être déplacée dans la portée du thread. Les valeurs accessibles depuis la table de hachage des paramètres de fonction `rss_feeds` nécessitent une copie locale avec `clone()`.\n\n![Terminal VS Code, création de thread avec clone](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_thread_spawn_clone.png){: .shadow}\n\n```rust\npub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n\n    // Store threads in vector\n    let mut threads: Vec\u003Cthread::JoinHandle\u003C()>> = Vec::new();\n\n    // Loop over rss_feeds and spawn threads\n    for (name, url) in rss_feeds {\n        let thread_name = name.clone();\n        let thread_url = url.clone();\n        let thread = thread::spawn(move || {\n            // Use thread_name and thread_url as values, see next chapter for instructions.\n```\n\n## Analyse du flux XML dans les types d'objets\nL'étape suivante consiste à analyser le flux RSS dans la fermeture du thread. Ajoutez les commentaires suivants dans le code pour guider les suggestions de code :\n\n1. `// Parse XML body with feed_rs parser, input in bytes` : récupérer le contenu de l'URL du flux RSS et l'analyser avec les fonctions de la crate `feed_rs`.\n2. `// Check feed_type attribute feed_rs::model::FeedType::RSS2 or Atom and print its name` : extraire le type de flux pour comparer l'attribut `feed_type` avec [`feed_rs::model::FeedType`](https://docs.rs/feed-rs/latest/feed_rs/model/enum.FeedType.html). Les suggestions de code doivent aussi recevoir des instructions avec les valeurs enum exactes à comparer.\n\n![Demande adressée aux suggestions de code de comparer les types de flux spécifiques](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_feed_rs_type_condition.png){: .shadow}\n\n```rust\n            // Parse XML body with feed_rs parser, input in bytes\n            let body = reqwest::blocking::get(thread_url).unwrap().bytes().unwrap();\n            let feed = feed_rs::parser::parse(body.as_ref()).unwrap();\n\n            // Check feed_type attribute feed_rs::model::FeedType::RSS2 or Atom and print its name\n            if feed.feed_type == feed_rs::model::FeedType::RSS2 {\n                println!(\"{} is an RSS2 feed\", thread_name);\n            } else if feed.feed_type == feed_rs::model::FeedType::Atom {\n                println!(\"{} is an Atom feed\", thread_name);\n            }\n```\n\nExécutez le programme et vérifiez les données de sortie.\n\n```\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\nCNCF is an RSS2 feed\nTechCrunch is an RSS2 feed\nGitLab Blog is an Atom feed\nHacker News is an RSS2 feed\n```\n\nPour cela, ouvrez les URL des flux dans le navigateur ou en inspectant les fichiers téléchargés.\n\nHacker News utilise RSS 2.0, avec `channel(title,link,description,item(title,link,pubDate,comments))`. TechCrunch et le blog de la CNCF ont une structure similaire.\n```xml\n\u003Crss version=\"2.0\">\u003Cchannel>\u003Ctitle>Hacker News\u003C/title>\u003Clink>https://news.ycombinator.com/\u003C/link>\u003Cdescription>Links for the intellectually curious, ranked by readers.\u003C/description>\u003Citem>\u003Ctitle>Writing a debugger from scratch: Breakpoints\u003C/title>\u003Clink>https://www.timdbg.com/posts/writing-a-debugger-from-scratch-part-5/\u003C/link>\u003CpubDate>Wed, 27 Sep 2023 06:31:25 +0000\u003C/pubDate>\u003Ccomments>https://news.ycombinator.com/item?id=37670938\u003C/comments>\u003Cdescription>\u003C![CDATA[\u003Ca href=\"https://news.ycombinator.com/item?id=37670938\">Comments\u003C/a>]]>\u003C/description>\u003C/item>\u003Citem>\n```\n\nLe blog de GitLab utilise le format de flux [Atom](https://datatracker.ietf.org/doc/html/rfc4287), similaire à RSS, mais nécessitant une logique d'analyse différente.\n```xml\n\u003C?xml version='1.0' encoding='utf-8' ?>\n\u003Cfeed xmlns='http://www.w3.org/2005/Atom'>\n\u003C!-- / Get release posts -->\n\u003C!-- / Get blog posts -->\n\u003Ctitle>GitLab\u003C/title>\n\u003Cid>https://about.gitlab.com/blog\u003C/id>\n\u003Clink href='https://about.gitlab.com/blog/' />\n\u003Cupdated>2023-09-26T00:00:00+00:00\u003C/updated>\n\u003Cauthor>\n\u003Cname>The GitLab Team\u003C/name>\n\u003C/author>\n\u003Centry>\n\u003Ctitle>Atlassian Server ending: Goodbye disjointed toolchain, hello DevSecOps platform\u003C/title>\n\u003Clink href='https://about.gitlab.com/blog/atlassian-server-ending-move-to-a-single-devsecops-platform/' rel='alternate' />\n\u003Cid>https://about.gitlab.com/blog/atlassian-server-ending-move-to-a-single-devsecops-platform/\u003C/id>\n\u003Cpublished>2023-09-26T00:00:00+00:00\u003C/published>\n\u003Cupdated>2023-09-26T00:00:00+00:00\u003C/updated>\n\u003Cauthor>\n\u003Cname>Dave Steer, Justin Farris\u003C/name>\n\u003C/author>\n```\n\n### Cartographie des types de données de flux génériques\nPour utiliser la fonction [`roxmltree::Document::parse`](https://docs.rs/roxmltree/latest/roxmltree/struct.Document.html) vous devez comprendre l'arborescence des nœuds XML et ses noms de balise. Heureusement, [feed_rs::model::Feed](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Feed.html) gère à la fois les flux RSS et Atom. Nous pouvons donc continuer avec la crate `feed_rs`.\n\n1. Atom : Feed->Feed, Entry->Entry\n2. RSS : Channel->Feed, Item->Entry\n\nNous devons extraire les attributs requis et associer leurs types de données. Consultez la [documentation feed_rs::model](https://docs.rs/feed-rs/latest/feed_rs/model/index.html) pour comprendre les structures, leurs champs et implémentations. Cela évitera les erreurs de conversion et les échecs de compilation, spécifiques à l'implémentation de `feed_rs`.\n\nLa structure [`Feed`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Feed.html) fournit un `title` de type `Option\u003CText>` (avec une valeur ou vide). La structure [`Entry`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Entry.html) fournit les éléments suivants :\n\n1. `title` : `Option\u003CText>` avec [`Text`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Text.html) et le champ `content` comme la `String`.\n2. `updated` : `Option\u003CDateTime\u003CUtc>>` avec [`DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) avec la [méthode `format()`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html#method.format).\n3. `summary` : `Option\u003CText>` [`Text`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Text.html) et le champ `content` comme la `String`.\n4. `links` : `Vec\u003CLink>`, vecteur avec les éléments [`Link`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Link.html). L'attribut `href` fournit la chaîne d'URL brute.\n\nUtilisez ces connaissances pour extraire les données des intrants de flux. Appelez `unwrap()` sur tous les types `Option` et ajoutez des instructions explicites aux suggestions de code.\n\n```rust\n                // https://docs.rs/feed-rs/latest/feed_rs/model/struct.Feed.html\n                // https://docs.rs/feed-rs/latest/feed_rs/model/struct.Entry.html\n                // Loop over all entries, and print\n                // title.unwrap().content\n                // published.unwrap().format\n                // summary.unwrap().content\n                // links href as joined string\n                for entry in feed.entries {\n                    println!(\"Title: {}\", entry.title.unwrap().content);\n                    println!(\"Published: {}\", entry.published.unwrap().format(\"%Y-%m-%d %H:%M:%S\"));\n                    println!(\"Summary: {}\", entry.summary.unwrap().content);\n                    println!(\"Links: {:?}\", entry.links.iter().map(|link| link.href.clone()).collect::\u003CVec\u003CString>>().join(\", \"));\n                    println!();\n                }\n```\n\n![Suggestions de code pour afficher les types d'éléments de flux, avec des exigences spécifiques](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_print_feed_entries_fields_with_rust_type_specifics.png){: .shadow}\n\n### Gestion des erreurs avec l'Option unwrap()\nCompilez, exécutez le programme, puis continuez avec des instructions multilignes. Remarque : `unwrap()` appelle la macro `panic!` et bloque le programme si des valeurs sont vides, comme un champ `summary` non défini dans les données du flux.\n\n```shell\nGitLab Blog is an Atom feed\nTitle: How the Colmena project uses GitLab to support citizen journalists\nPublished: 2023-09-27 00:00:00\nthread '\u003Cunnamed>' panicked at 'called `Option::unwrap()` on a `None` value', src/feed_reader.rs:40:59\n```\n\nUtilisez [`std::Option::unwrap_or_else`](https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap_or_else) pour définir une chaîne vide comme valeur par défaut. La syntaxe nécessite une fermeture qui renvoie une instanciation de structure `Text` vide.\n\nJ'ai dû m'y reprendre à plusieurs fois pour trouver l'initialisation correcte permettant de résoudre le problème, car le passage d'une chaîne vide ne fonctionne pas avec les types personnalisés. Je vais vous montrer mes tentatives et recherches.\n\n```rust\n// Problem: The `summary` attribute is not always initialized. unwrap() will panic! then.\n// Requires use mime; and use feed_rs::model::Text;\n/*\n// 1st attempt: Use unwrap() to extraxt Text from Option\u003CText> type.\nprintln!(\"Summary: {}\", entry.summary.unwrap().content);\n// 2nd attempt. Learned about unwrap_or_else, passing an empty string.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| \"\").content);\n// 3rd attempt. summary is of the Text type, pass a new struct instantiation.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{}).content);\n// 4th attempt. Struct instantiation requires 3 field values.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{\"\", \"\", \"\"}).content);\n// 5th attempt. Struct instantation with public fields requires key: value syntax\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: \"\", src: \"\", content: \"\"}).content);\n// 6th attempt. Reviewed expected Text types in https://docs.rs/feed-rs/latest/feed_rs/model/struct.Text.html and created Mime and String objects\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: String::new(), content: String::new()}).content);\n// 7th attempt: String and Option\u003CString> cannot be casted automagically. Compiler suggested using `Option::Some()`.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: Option::Some(), content: String::new()}).content);\n*/\n\n// xth attempt: Solution. Option::Some() requires a new String object.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: Option::Some(String::new()), content: String::new()}).content);\n```\n\nCette approche n'était pas satisfaisante, car la ligne de code était difficile à lire et nécessitait une retouche manuelle sans suggestions de code. Puis, j'ai trouvé où se nichait le problème : si `Option` a la valeur `none`, `unwrap()` génère une erreur. J'ai demandé aux suggestions de code s’il existe une méthode plus simple, avec ce nouveau commentaire :\n\n```\n                // xth attempt: Solution. Option::Some() requires a new String object.\n                println!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: Option::Some(String::new()), content: String::new()}).content);\n\n                // Alternatively, use Option.is_none()\n```\n\n![Demande aux suggestions de code d'une alternative pour Options.is_none](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_after_complex_unwrap_or_else_ask_for_alternative_option.png){: .shadow}\n\nCette méthode améliore la lisibilité, moins de cycles CPU sont gaspillés sur `unwrap()` et la courbe d'apprentissage est excellente, de la résolution d'un problème complexe à l'utilisation d'une solution standard. Gagnant-gagnant.\n\nAjoutez le stockage des données XML sur le disque pour finaliser l'application de lecteur de flux.\n\n```rust\n                // Dump the parsed body to a file, as name-current-iso-date.xml\n                let file_name = format!(\"{}-{}.xml\", thread_name, chrono::Local::now().format(\"%Y-%m-%d-%H-%M-%S\"));\n                let mut file = std::fs::File::create(file_name).unwrap();\n                file.write_all(body.as_ref()).unwrap();\n```\n\nCompilez et exécutez le programme pour vérifier les données de sortie.\n\n```shell\ncargo build\n\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\n![Terminal VS Code, exécution de Cargo avec des données de sortie d'intrants au format requis](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_cargo_run_formatted_output_final.png)\n\n## Benchmarks\n\n### Exécution séquentielle vs parallèle\nComparez les temps d'exécution du code en créant cinq échantillons pour chaque type d'exécution.\n\n1. Exécution séquentielle. [Exemple de code source de la MR](https://gitlab.com/gitlab-de/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader/-/merge_requests/1)\n2. Exécution parallèle. [Exemple de code source de la MR](https://gitlab.com/gitlab-de/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader/-/merge_requests/3)\n\n```shell\n# Sequential\ngit checkout sequential-exec\n\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n0.21s user 0.08s system 10% cpu 2.898 total\n0.21s user 0.08s system 11% cpu 2.585 total\n0.21s user 0.09s system 10% cpu 2.946 total\n0.19s user 0.08s system 10% cpu 2.714 total\n0.20s user 0.10s system 10% cpu 2.808 total\n```\n\n```shell\n# Parallel\ngit checkout parallel-exec\n\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n0.19s user 0.08s system 17% cpu 1.515 total\n0.18s user 0.08s system 16% cpu 1.561 total\n0.18s user 0.07s system 17% cpu 1.414 total\n0.19s user 0.08s system 18% cpu 1.447 total\n0.17s user 0.08s system 16% cpu 1.453 total\n```\nAvec quatre threads RSS en parallèle, le temps total est presque réduit de moitié, mais le CPU est plus sollicité. Optimisons le code et les fonctionnalités.\n\nActuellement, nous utilisons la version de débogage Cargo, mais pas encore les versions optimisées. L'exécution parallèle a ses limites : les points de terminaison HTTP peuvent atteindre leur débit maximum, et \n\nchaque thread nécessite un changement de contexte dans le noyau, consommant des ressources. Lorsqu'un thread reçoit des ressources de calcul, d'autres sont mis en veille. Un trop grand nombre de threads peut ralentir le système. Pour éviter cela, utilisez des design patterns comme les [files d'attente de travail](https://docs.rs/work-queue/latest/work_queue/). Un pool de threads récupère cette tâche pour une exécution asynchrone.\n\nRust garantir la synchronisation des données entre les threads à l'aide des [canaux](https://doc.rust-lang.org/rust-by-example/std_misc/channels.html). Pour garantir un accès simultané aux données, des [mutexes (exclusions mutuelles)](https://doc.rust-lang.org/std/sync/struct.Mutex.html) fournissent des verrous sûrs.\n\n### CI/CD avec mise en cache Rust\nAjoutez la configuration CI/CD suivante dans le fichier `.gitlab-ci.yml`. Le job `run-latest` appelle `cargo run` avec des exemples d'URL de flux RSS et mesure le temps d'exécution en continu.\n\n```\nstages:\n  - build\n  - test\n  - run\n\ndefault:\n  image: rust:latest\n  cache:\n    key: ${CI_COMMIT_REF_SLUG}\n    paths:\n      - .cargo/bin\n      - .cargo/registry/index\n      - .cargo/registry/cache\n      - target/debug/deps\n      - target/debug/build\n    policy: pull-push\n\n# Cargo data needs to be in the project directory for being cached.\nvariables:\n  CARGO_HOME: ${CI_PROJECT_DIR}/.cargo\n\nbuild-latest:\n  stage: build\n  script:\n    - cargo build --verbose\n\ntest-latest:\n  stage: build\n  script:\n    - cargo test --verbose\n\nrun-latest:\n  stage: run\n  script:\n    - time cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\n![Pipelines GitLab CI/CD pour Rust, données de sortie de l'exécution Cargo](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/gitlab_cicd_pipeline_rust_cargo_run_output.png){: .shadow}\n\n## Étapes suivantes\nRédiger cet article a été un défi pour maîtriser les techniques avancées de Rust et optimiser les suggestions de code. Ces dernières sont très utiles pour générer rapidement du code, avec le contexte local et pour ajuster l'algorithme à mesure que vous écrivez le code. Dans cet article, j'ai partagé les défis et les solutions. L'exemple de code pour l'application de lecture est disponible dans [le projet learn-rust-ai-app-reader](https://gitlab.com/gitlab-de/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader).\n\nL'analyse des flux RSS étant complexe, elle nécessite des structures de données, des requêtes HTTP externes et des optimisations parallèles. Alors, `pourquoi ne pas utiliser la crate std::rss` -- pour une exécution asynchrone avancée. Cela ne démontre pas toutes les fonctionnalités de Rust abordées dans cet article, mais vous pouvez essayer de réécrire le code avec la [crate `rss`](https://docs.rs/rss/latest/rss/) pour pratiquer l'exécution asynchrone.\n\n### Exercices d'apprentissage de l'exécution asynchrone\nCet article pose les bases pour explorer le stockage persistant et la présentation des données. Pour vous perfectionner votre application en Rust, voici quelques idées :\n\n1. Stockage des données : utilisez une base de données comme sqlite et suivez les mises à jour des flux RSS.\n2. Notifications : déclenchez des notifications dans Telegram, ou autres.\n3. Fonctionnalités : étendez les types de lecteurs aux [API REST](https://about.gitlab.com/fr-fr/blog/what-is-rest-api/ \"API REST\")\n4. Configuration : intégrez des fichiers de configuration pour les flux RSS, les API, etc.\n5. Efficacité : ajoutez des filtres et des balises d'abonnement.\n6. Déploiement : utilisez un serveur Web, collectez les indicateurs avec Prometheus et déployez sur Kubernetes.\n\nNous aborderons certains de ces sujets dans un prochain article. En attendant, essayez d'implémenter des flux RSS existants et explorez d'autres bibliothèques Rust (`crates`).\n\n### Votre retour d'expérience\nLorsque vous utilisez les suggestions de code de [GitLab Duo](/gitlab-duo/), n'hésitez pas à [partager vos retours dans le ticket prévu à cet effet](https://gitlab.com/gitlab-org/gitlab/-/issues/405152).\n","ai-ml",[711,9,712,713,714],"DevSecOps","tutorial","workflow","AI/ML","2025-01-27",{"slug":717,"featured":6,"template":691},"learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions","content:fr-fr:blog:learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions.yml","Learn Advanced Rust Programming With A Little Help From Ai Code Suggestions","fr-fr/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions.yml","fr-fr/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions",{"_path":723,"_dir":246,"_draft":6,"_partial":6,"_locale":7,"seo":724,"content":730,"config":739,"_id":741,"_type":13,"title":742,"_source":15,"_file":743,"_stem":744,"_extension":18},"/fr-fr/blog/we-need-to-talk-no-proxy",{"title":725,"description":726,"ogTitle":725,"ogDescription":726,"noIndex":6,"ogImage":727,"ogUrl":728,"ogSiteName":674,"ogType":675,"canonicalUrls":728,"schema":729},"Pouvons-nous standardiser la variable d'environnement NO_PROXY ?","Découvrez notre guide complet sur no_proxy, la configuration des paramètres\ndu proxy, ainsi que les spécificités de no_proxy, http_proxy et https_proxy.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659507/Blog/Hero%20Images/AdobeStock_623844718.jpg","https://about.gitlab.com/blog/we-need-to-talk-no-proxy","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Pouvons-nous standardiser la variable d'environnement NO_PROXY ?\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Stan Hu\"}],\n        \"datePublished\": \"2021-01-27\",\n      }",{"title":725,"description":726,"authors":731,"heroImage":727,"date":733,"body":734,"category":735,"tags":736},[732],"Stan Hu","2021-01-27","Si vous avez déjà utilisé un serveur proxy Web, vous êtes probablement familier avec les variables d’environnement `http_proxy` ou `HTTP_PROXY`. Cependant, vous l'êtes peut-être moins avec la variable `no_proxy` qui permet d’exclure le trafic destiné à certains hôtes d'utiliser le proxy. Bien que le protocole HTTP soit standardisé, aucune norme ne précise comment utiliser ces variables. Par conséquent, les clients Web prennent en charge ces variables de manière différente.  \n\nDécouvrez dans cet article tout ce que vous devez savoir sur la variable d’environnement no_proxy, ainsi qu’un cas pratique d’un de nos clients GitLab. \n\n## Comprendre http_proxy, https_proxy et no_proxy\n\nDe nos jours, la plupart des clients Web permettent de se connecter à des serveurs proxy via les variables d'environnement suivantes : \n\n- `http_proxy` / `HTTP_PROXY`\n- `https_proxy` / `HTTPS_PROXY`\n- `no_proxy` / `NO_PROXY`\n\nCes variables indiquent au client l'URL à utiliser pour accéder aux serveurs proxy et quelles exceptions appliquer. \n\nPar exemple, si vous avez un serveur proxy attaché à `http://alice.example.com:8080`, vous pourriez l’utiliser via : \n\n```sh\nexport http_proxy=http://alice.example.com:8080\n```\n\nMais quel serveur proxy sera utilisé si Maxime définit aussi la version en majuscules : `HTTP_PROXY` ?\n\n```sh\nexport HTTP_PROXY=http://maxime.example.com:8080\n```\nAussi surprenant que cela puisse paraître, cela dépend. Dans certains cas, le proxy d'Alice est utilisé, dans d'autres cas, c'est celui de Maxime. Nous y reviendrons plus loin dans cet article. \n\nQue se passe-t-il si vous souhaitez définir des exceptions ? Par exemple, admettons que vous souhaitez utiliser un serveur proxy pour tout, sauf pour `internal.example.com` et `internal2.example.com`. C'est ici que la variable `no_proxy` entre en jeu. Vous paramétrez alors `no_proxy` comme suit : \n\n```sh\nexport no_proxy=internal.example.com,internal2.example.com\n```\n\nEt si vous souhaitez exclure certaines adresses IP ? Faut-il utiliser des astérisques ou des caractères génériques ? Ou bien utiliser des blocs CIDR (comme `192.168.1.1/32`) ? Même réponse : cela dépend.\n\n## L'évolution des variables proxy et no_proxy\n\nEn 1994, la plupart des clients Web utilisaient la bibliothèque logicielle `libwww` du CERN, qui prenait en charge les variables d'environnement `http_proxy` et `no_proxy`. `libwww` utilisait uniquement la forme en minuscules de `http_proxy` et [la syntaxe de `no_proxy` était simple](https://github.com/w3c/libwww/blob/8678b3dcb4191065ca39caea54bb1beba809a617/Library/src/HTAccess.c#L234-L239) : \n\n```\nno_proxy is a comma- or space-separated list of machine\nor domain names, with optional :port part.  If no :port\npart is present, it applies to all ports on that domain.\n\nExample:\n\t\tno_proxy=\"cern.ch,some.domain:8001\"\n```\n\nDe nouveaux clients sont apparus, ajoutant leurs propres implémentations HTTP sans les relier à `libwww`. En janvier 1996, Hrvoje Niksic publie `geturl`, le prédécesseur de ce qu'on connaît aujourd'hui sous le nom de `wget`. Un mois plus tard, [`geturl` prend en charge `http_proxy` dans la version v1.1](https://ftp.sunet.se/mirror/archive/ftp.sunet.se/pub/www/utilities/wget/old-versions/). En mai 1996, `geturl` v1.3 ajoute la prise en charge de `no_proxy`. Tout comme `libwww`, `geturl` n'accepte que les minuscules. \n\nEn janvier 1998, Daniel Stenberg publie `curl` v5.1, [qui prend en charge les variables `http_proxy` et `no_proxy`](https://github.com/curl/curl/blob/ae1912cb0d494b48d514d937826c9fe83ec96c4d/CHANGES#L929-L944). De plus, `curl` accepte désormais les majuscules, `HTTP_PROXY` et `NO_PROXY`.\n\n__Nota Bene :__ en mars 2009, [`curl` v7.19.4](https://github.com/curl/curl/releases/tag/curl-7_19_4) abandonne la prise en compte de la forme en majuscules de `HTTP_PROXY` [en raison de problèmes de sécurité](https://github.com/curl/curl/blob/30e7641d7d2eb46c0b67c0c495a0ea7e52333ee2/lib/url.c#L2250-L2261). Cependant, même si `curl` n'accepte plus `HTTP_PROXY`, `HTTPS_PROXY` fonctionne toujours.\n\n## Gestion des variables du serveur proxy\n\nSuite aux [recherches de notre collègue Nourdin el Bacha](https://gitlab.com/gitlab-com/support/support-team-meta/-/issues/2991), nous comprenons que la gestion des variables du serveur proxy varie en fonction du langage ou de l'outil utilisé. \n\n### http_proxy et https_proxy\n\nDans ce tableau, chaque ligne représente une variable, et chaque colonne correspond à l'outil (comme `curl`) ou au langage (comme `Ruby`) auquel elle s'applique : \n\n|                 | curl      | wget           | Ruby          | Python    | Go        |\n|-----------------|-----------|----------------|---------------|-----------|-----------|\n| `http_proxy`    | Oui       | Oui            | Oui           | Oui       | Oui        |\n| `HTTP_PROXY`    | Non        | Non           | Oui ([Mise en garde](https://github.com/ruby/ruby/blob/0ed71b37fa9af134fdd5a7fd1cebd171eba83541/lib/uri/generic.rb#L1519)) | Oui (si `REQUEST_METHOD` n'est pas dans env)       | Oui       |\n| `https_proxy`   | Oui       | Oui            | Oui           | Oui       | Oui       |\n| `HTTPS_PROXY`   | Oui       | Non             | Oui           | Oui       | Oui       |\n| Casse de caractères | Minuscules | Minuscules uniquement | Minuscules     | Minuscules | Majuscules |\n| Sources       | [source](https://github.com/curl/curl/blob/30e7641d7d2eb46c0b67c0c495a0ea7e52333ee2/lib/url.c#L2250-L2266) | [source](https://github.com/jay/wget/blob/099d8ee3da3a6eea5635581ae517035165f400a5/src/retr.c#L1222-L1239) | [source](https://github.com/ruby/ruby/blob/0ed71b37fa9af134fdd5a7fd1cebd171eba83541/lib/uri/generic.rb#L1474-L1543) | [source](https://github.com/python/cpython/blob/030a713183084594659aefd77b76fe30178e23c8/Lib/urllib/request.py#L2488-L2517) | [source](https://github.com/golang/go/blob/682a1d2176b02337460aeede0ff9e49429525195/src/vendor/golang.org/x/net/http/httpproxy/proxy.go#L82-L97) |\n\nNotez que `http_proxy` et `https_proxy` sont toujours pris en charge, alors que `HTTP_PROXY` ne l'est pas. Python (via `urllib`) ne facilite pas les choses : `HTTP_PROXY` peut être utilisé [tant que `REQUEST_METHOD` n'est pas défini dans l'environnement](https://github.com/python/cpython/blob/030a713183084594659aefd77b76fe30178e23c8/Lib/urllib/request.py#L2504-L2508).\n\nLes variables d'environnement sont normalement définies en majuscules, mais puisque `http_proxy` est apparu en premier, il est devenu de fait la norme. En cas de doute, optez pour la forme en minuscules, car elle est universellement prise en charge.\n\nContrairement à la plupart des implémentations, Go essaie d'abord la forme en majuscules avant revenir à la version en minuscules. Nous verrons plus tard pourquoi cela a causé du tort à notre client GitLab. \n\n### no_proxy\n\nCertains utilisateurs ont signalé le [manque d'indications sur `no_proxy`](https://github.com/curl/curl/issues/1208). Puisque `no_proxy` définit une liste d'exceptions, des questions sur son fonctionnement se posent. \n\nPar exemple, vous configurez votre `no_proxy` comme suit : \n\n```sh\nexport no_proxy=example.com\n```\n\nEst-ce que cela signifie que le domaine doit être une correspondance exacte ou que `subdomain.example.com` corresponde aussi à cette configuration ? \n\nLe tableau suivant présente les différentes configurations. Toutes les implémentations correspondent aux suffixes, comme le montre la ligne « `correspondance des suffixes` ».\n\n|                       | curl      | wget           | Ruby      | Python    | Go        |\n|-----------------------|-----------|----------------|-----------|-----------|-----------|\n| `no_proxy`            | Oui       | Oui          | Oui     | Oui      | Oui      |\n| `NO_PROXY`            | Oui       | Non             | Oui       | Oui       | Oui       |\n| Casse de caractères       | Minuscules | Minuscules uniquement | Minuscules | Minuscules | Majuscules |\n| Correspondance des suffixes ?     | Oui       | Oui            | Oui       | Oui       | Oui       |\n| Points initiaux `.` ?   | Oui       | Non             | Oui       | Oui       | Non        |\n| `*` fait correspondre tous les hôtes| Oui       | Non             | Non        | Oui       | Oui       |\n| Prise en charge des expressions régulières ?     | Non        | Non             | Non        | Non        | Non        |\n| Prise en charge des blocs CIDR ? | Non        | Non             | Oui       | Non        | Oui       |\n| Détection des adresses IP de bouclage ? | Non        | Non             | Non        | Non        | Oui       |\n| Sources            | [source](https://github.com/curl/curl/blob/30e7641d7d2eb46c0b67c0c495a0ea7e52333ee2/lib/url.c#L2152-L2206) | [source](https://github.com/jay/wget/blob/099d8ee3da3a6eea5635581ae517035165f400a5/src/retr.c#L1266-L1274) | [source](https://github.com/ruby/ruby/blob/0ed71b37fa9af134fdd5a7fd1cebd171eba83541/lib/uri/generic.rb#L1545-L1554) | [source](https://github.com/python/cpython/blob/030a713183084594659aefd77b76fe30178e23c8/Lib/urllib/request.py#L2519-L2551)| [source](https://github.com/golang/go/blob/682a1d2176b02337460aeede0ff9e49429525195/src/vendor/golang.org/x/net/http/httpproxy/proxy.go#L170-L206) |\n\nCependant, si `no_proxy` est précédé d'un `.`, cela implique des changements. \n\nPar exemple, `curl` et `wget` se comportent différemment. `curl` supprime le `.` dans la configuration pour se coller au suffixe du domaine. Il contourne ainsi le proxy :\n\n```sh\n$ env https_proxy=http://non.existent/ no_proxy=.gitlab.com curl https://gitlab.com\n\u003Chtml>\u003Cbody>You are being \u003Ca href=\"https://about.gitlab.com/\">redirected\u003C/a>.\u003C/body>\u003C/html>\n```\n\nNéanmoins, `wget` ne supprime pas le `.` et utilise la correspondance exacte avec le nom d'hôte. Par conséquent, `wget` essaie d'utiliser un proxy si un domaine de premier niveau est utilisé : \n\n```sh\n$ env https_proxy=http://non.existent/ no_proxy=.gitlab.com wget https://gitlab.com\nResolving non.existent (non.existent)... failed: Name or service not known.\nwget: unable to resolve host address 'non.existent'\n```\n\nToutes les implémentations ne prennent pas en charge les expressions régulières (regex). Utiliser des regex peut compliquer la configuration puisqu'elles peuvent prendre différentes variantes (comme PCRE ou POSIX). L'utilisation d'expressions régulières entraîne également de potentielles failles de sécurité et de performance. \n\nConfigurer `no_proxy` avec un astérisque (`*`) peut désactiver l'utilisation de proxy pour toutes les adresses, bien que ce principe ne soit pas appliqué tout le temps. \n\nAu moment de décider de l'utilisation d'un proxy, aucune implémentation n'effectue de recherche DNS pour résoudre un nom d'hôte en adresse IP. Il est préférable d'éviter de spécifier les adresses IP dans `no_proxy` à moins que le client ne les utilise explicitement. \n\nIl en va de même pour les blocs CIDR, tels que `18.240.0.1/24`. Ces blocs ne fonctionnent que si la requête est faite directement à une adresse IP. Seuls les environnements de développement Go et Ruby permettent l'utilisation de blocs CIDR. Go désactive même automatiquement l'utilisation d'un proxy si une adresse IP de bouclage est détectée, ce qui n'est pas le cas dans d'autres implémentations.\n\n## Pourquoi les paramètres du proxy sont-ils importants ?\n\nSi votre application est codée en plusieurs langages et doit fonctionner avec un pare-feu avec un serveur proxy, il est important de faire attention à ces différences. \n\nPar exemple, GitLab est composé d'éléments codés en Ruby et d'autres en Go. Un de nos clients a configuré son proxy de la façon suivante : \n\n```yaml\nHTTP_PROXY: http://proxy.company.com\nHTTPS_PROXY: http://proxy.company.com\nNO_PROXY: .correct-company.com\n```\n\nIl a ensuite signalé un problème avec GitLab : \n- Un `git push` à partir de la ligne de commande a fonctionné.\n- Les modifications Git effectuées à partir de l'interface Web ont échoué.\n\nNos ingénieurs ont découvert qu'un problème de configuration de [Kubernetes](https://about.gitlab.com/fr-fr/blog/kubernetes-the-container-orchestration-solution/ \"Kubernetes\") entraînait le maintien de valeurs obsolètes. L'environnement ressemblait à : \n\n```yaml\nHTTP_PROXY: http://proxy.company.com\nHTTPS_PROXY: http://proxy.company.com\nNO_PROXY: .correct-company.com\nno_proxy: .wrong-company.com\n```\n\nLes irrégularités entre `no_proxy` et `NO_PROXY` ont entraîné des alertes. Supprimer l'entrée incorrecte ou uniformiser les variables auraient pu résoudre le problème. Observons ce qu'il s'est passé. \n\nRappelez-vous que : \n- Ruby priorise `no_proxy`.\n- Go priorise `NO_PROXY`.\n\nPar conséquent, les services codées en Go, comme [GitLab Workhorse](https://docs.gitlab.com/ee/development/workhorse/), ont un bon paramétrage du proxy. Un `git push` depuis la ligne de commande a fonctionné car les services Go gèrent cette activité : \n\n```mermaid\nsequenceDiagram\n    autonumber\n    participant C as Client\n    participant W as Workhorse\n    participant G as Gitaly\n    C->>W: git push\n    W->>G: gRPC: PostReceivePack\n    G->>W: OK\n    W->>C: OK\n```\n\nL'appel gRPC de l'étape 2 n'a jamais tenté d'utiliser le proxy car `no_proxy` a été configuré correctement pour se connecter directement à Gitaly.\n\nCependant, lorsqu'un utilisateur effectue une modification dans l'interface utilisateur, Gitaly transmet la requête au service `gitaly-ruby`, qui est écrit en Ruby. `gitaly-ruby` effectue des modifications dans le dépôt et renvoie un rapport via un appel gRPC à son processus parent. Malheureusement, comme le montre l'étape 4 ci-dessous, l'étape de reporting n'a pas eu lieu :\n\n```mermaid\nsequenceDiagram\n    autonumber\n    participant C as Client\n    participant R as Rails\n    participant G as Gitaly\n    participant GR as gitaly-ruby\n    participant P as Proxy\n    C->>R: Change file in UI\n    R->>G: gRPC: UserCommitFiles\n    G->>GR: gRPC: UserCommitFiles\n    GR->>P: CONNECT\n    P->>GR: FAIL\n```\n\nPuisque gRPC utilise HTTP/2 comme mode de transport, `gitaly-ruby` tente de se connecter au proxy (il était connecté à une configuration erronée de `no_proxy`). Le proxy a immédiatement rejeté la requête HTTP, ce qui a entraîné l'erreur du push sur l'interface Web. \n\nAprès la suppression des minuscules `no_proxy` de l'environnement, les pushs depuis l'interface utilisateur ont fonctionné comme prévu. `gitaly-ruby` s'est connecté au processus parent Gitaly. L'étape 4 a donc fonctionné comme suit : \n\n```mermaid\nsequenceDiagram\n    autonumber\n    participant C as Client\n    participant R as Rails\n    participant G as Gitaly\n    participant GR as gitaly-ruby\n    participant P as Proxy\n    C->>R: Change file in UI\n    R->>G: gRPC: UserCommitFiles\n    G->>GR: gRPC: UserCommitFiles\n    GR->>G: OK\n    G->>R: OK\n    R->>C: OK\n```\n\n#### Pourquoi faut-il privilégier un proxy HTTPS ?\n\nNotez que le client a défini `HTTPS_PROXY` sur un proxy HTTP non crypté : `http://` est préféré à `https://`. Bien que ce ne soit pas idéal en termes de sécurité, certaines équipes de développement utilisent cette méthode pour éviter les problèmes de certificats TLS (et donc, des problèmes de connexion pour les utilisateurs finaux). \n\nSi un proxy HTTPS avait été spécifié, nous n’aurions pas rencontré ce problème. Lorsqu'un proxy HTTPS est utilisé, gRPC ignore ce paramètre car [les proxy HTTPS ne sont pas pris en charge](https://github.com/grpc/grpc/issues/20939).\n\n## Le plus petit dénominateur commun\n\nPersonne ne devrait définir des valeurs irrégulières avec des paramètres de proxy en minuscules et en majuscules. Cependant, si vous devez gérer une stack dans plusieurs langages, vous pourrez configurer les paramètres de proxy HTTP selon le plus petit dénominateur commun.\n\n### `http_proxy` et `https_proxy`\n\n`HTTP_PROXY` n'est pas toujours pris en charge ou recommandé. Préférez toujours le format en minuscules et si vous devez absolument utiliser la version en majuscules, vérifiez qu'elles partagent la même valeur.\n\n### `no_proxy`\n\n1. Adoptez le format en minuscules.\n2. Utilisez les valeurs `hostname:port` séparées par des virgules.\n3. Les adresses IP sont acceptables, mais les noms d'hôtes ne sont pas résolus.\n4. Les suffixes correspondent toujours (`example.com` correspondra à `test.example.com`).\n5. Évitez d'utiliser le point initial (`.`) pour les domaines de premier niveau.\n6. Veillez à ne pas utiliser de correspondances de blocs CIDR. Seuls Go et Ruby les prennent en charge. \n\n## Standardiser `no_proxy`\n\nConnaître le plus petit dénominateur commun aide à éviter des problèmes si ces définitions sont copiées pour différents clients Web. Mais est-ce que `no_proxy` et les autres configurations de proxy requièrent une version standard documentée plutôt qu'une convention ad hoc ? \n\nVoici quelques points qui peuvent vous aider : \n\n1. Préférez le format en minuscules aux variantes en majuscules (`http_proxy` devrait être utilisé avant `HTTP_PROXY`).\n2. Utilisez des valeurs `hostname:port` séparées par des virgules (chaque valeur peut inclure des espaces facultatifs.). \n3. Ne faites pas de recherche DNS et évitez les expressions régulières (regex). \n4. Appliquez `*` pour faire correspondre tous les hôtes.\n5. Supprimez les points initiaux (`.`) et faites correspondre les suffixes de domaine. \n6. Prenez en charge la correspondance des blocs CIDR. \n7. Évitez les suppositions sur des adresses IP spéciales (comme les adresses de bouclage dans `no_proxy`).\n\n## Conclusion\n\nPlus de 30 ans se sont écoulés depuis la sortie du premier serveur proxy Web. Depuis, les principes fondamentaux pour configurer un client Web grâce à des variables n'ont pas vraiment changé. Néanmoins, certaines subtilités ont vu le jour. \n\nÀ travers un cas pratique, vous avez découvert qu'une définition erronée des variables `no_proxy` et `NO_PROXY` a entraîné des heures de travail pour résoudre le problème, en raison de différences d'acceptation de configuration entre Ruby et Go. \n\nEn mettant en évidence ces différences, nous espérons vous éviter de futurs problèmes dans votre stack de production. Et qui sait, nous verrons peut-être voir le jour une standardisation au niveau des chargés de maintenance des clients Web. \n","engineering",[269,9,737,738],"user stories","startups",{"slug":740,"featured":6,"template":691},"we-need-to-talk-no-proxy","content:fr-fr:blog:we-need-to-talk-no-proxy.yml","We Need To Talk No Proxy","fr-fr/blog/we-need-to-talk-no-proxy.yml","fr-fr/blog/we-need-to-talk-no-proxy",{"_path":746,"_dir":246,"_draft":6,"_partial":6,"_locale":7,"seo":747,"content":753,"config":761,"_id":763,"_type":13,"title":764,"_source":15,"_file":765,"_stem":766,"_extension":18},"/fr-fr/blog/beginner-guide-python-programming",{"title":748,"description":749,"ogTitle":748,"ogDescription":749,"noIndex":6,"ogImage":750,"ogUrl":751,"ogSiteName":674,"ogType":675,"canonicalUrls":751,"schema":752},"Apprendre Python : tout savoir sur ce langage de programmation","Python est de plus en plus populaire, et pour de bonnes raisons. Retrouvez toutes les informations essentielles pour faire vos premiers pas avec Python.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664962/Blog/Hero%20Images/python.jpg","https://about.gitlab.com/blog/beginner-guide-python-programming","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Apprendre Python : tout savoir sur ce langage de programmation\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"GitLab\"}],\n        \"datePublished\": \"2021-10-21\",\n      }",{"title":748,"description":749,"authors":754,"heroImage":750,"date":756,"body":757,"category":758,"tags":759},[755],"GitLab","2021-10-21","Vous vous passionnez pour la programmation informatique et souhaitez en apprendre davantage sur Python ? Vous souhaitez savoir par où commencer pour maîtriser les bases de Python ? Découvrez dans cet article les concepts de base de ce langage. \n\n## Comment débuter sur Python ?\n\nPython est un langage de programmation puissant et facile à apprendre, à utiliser et à déployer, dont l'utilisation est très répandue dans la création d'applications web et de bureau. Python facilite également l'analyse de données et l'exécution de tâches [DevOps](https://about.gitlab.com/fr-fr/solutions/devops-platform/ \"Qu'est-ce que DevOps ? \"). C'est un langage de programmation gratuit, open source et orienté objet, utilisé pour écrire des scripts simples, tout comme des programmes complexes. Parmi les près de 700 langages de programmation, Python se distingue comme l'un des plus accessibles pour les débutants.\n\n### Installer Python\n\nAvant d'aborder les bases de Python, il est essentiel de télécharger et d'installer Python sur votre ordinateur de bureau ou votre ordinateur portable. Python fonctionne sur plusieurs plateformes, dont Linux, Windows et Mac. Il est préinstallé sur la plupart des systèmes Mac et Linux, mais il est préférable de télécharger la dernière version depuis le [site officiel de Python](https://www.python.org/downloads/ \"Site officiel de Python\").\n\nPour vérifier la version actuelle de Python sur votre système, ouvrez la ligne de commande et tapez « python -V ».\n\n![Invite de commande Python](https://about.gitlab.com/images/blogimages/python1.png){: .shadow}\n\nSi votre version est obsolète, téléchargez l'installation 32 ou 64 bits à partir du site Web, selon la configuration de votre système.\n\nIl existe d’autres alternatives pour télécharger le programme d’installation : sur Windows, vous pouvez l'installer directement à partir de Microsoft. Sur Linux, vous pouvez l'installer à l'aide du gestionnaire de paquets. Sur macOS, vous pouvez télécharger Python à partir de [Homebrew](https://brew.sh/ \"Homebrew\").\n\nUne fois le programme téléchargé, exécutez-le et confirmez l'installation du fichier. Vous pouvez dès lors commencer à utiliser Python. \n\nVoici un exemple d'installation de Python pour Windows.\n\n![Installation de Python pour Windows ](https://about.gitlab.com/images/blogimages/python2.png){: .shadow}\n\n### Exécuter Python dans l'invite de commande\n\nPour vérifier que Python est installé et fonctionne correctement sous Windows, ouvrez l'invite de commande et saisissez « python », ce qui lancera l'interpréteur. Vous pouvez y exécuter directement des codes Python. Par exemple, saisissez « 2*5+1 » et appuyez sur « Entrée ». Vous verrez « 11 » comme résultat. Saisissez « Quit () » pour quitter l'interpréteur.\n\n![Interpréteur Python](https://about.gitlab.com/images/blogimages/python3.png){: .shadow}\n\n### Exécuter Python dans l'IDE\n\nMaintenant que la dernière version de Python est installée, vous êtes prêt à commencer à programmer en Python. Pour écrire des scripts ou des programmes longs en Python, utilisez l'environnement de développement et d'apprentissage intégré ([IDLE](https://docs.python.org/fr/3/library/idle.html \"IDLE Python\")) de Python.\n\nLancez l'IDLE puis, dans le menu déroulant sélectionnez « Fichier », puis « Nouveau fichier », ce qui ouvre une nouvelle fenêtre d'édition. Vous avez à présent deux fenêtres sur votre écran : un *shell* Python et un fichier sans titre.\n\n![shell Python et fichier sans titre](https://about.gitlab.com/images/blogimages/python4.png){: .shadow}\n\nLe *shell* Python est un environnement REPL, abréviation de « *read-eval-print-loop* » (boucle de lecture-évaluation-affichage). Il exécute des extraits de code, généralement une instruction à la fois. Par exemple, en répétant le même calcul « 2*5+1 » que nous avons fait dans l'invite de commande, vous pouvez constater qu'un *shell* Python peut fonctionner comme une calculatrice.\n\n![Débuter sur Python avec une opération de calcul](https://about.gitlab.com/images/blogimages/python5.png){: .shadow}\n\nLa fenêtre sans titre est une fenêtre d'édition de texte permettant d'écrire des programmes complets. Le *shell* affiche son résultat. Par exemple, le premier programme conventionnel de Python pour les débutants consiste à afficher le texte « Hello World! ». Veillez à sauvegarder l'éditeur de texte avant de l'exécuter en appuyant sur « F5 ».\n\n![Affichage du texte Hello World! sur Python](https://about.gitlab.com/images/blogimages/python61.png){: .shadow}\n\n## Les bases de Python\n\nNous savons que vous avez hâte de commencer à écrire de longs scripts pour des jeux et des sites Web, mais il vous reste encore un long chemin à parcourir pour y arriver. Comme pour l’apprentissage de tout autre langage, vous devez d'abord comprendre les bases de Python.\n\nLa fonction __print()__, comme nous l’avons vu dans l'exemple « Hello World! », affiche une valeur dans la fenêtre de résultat. Une valeur est la chose la plus élémentaire qu'un programme utilise. Il peut s'agir d'une chaîne de caractères, d'une valeur numérique ou de tout autre objet Python. Tout objet entre guillemets simples ou doubles est appelé chaîne de caractères (string).\n\nPar exemple, le « Hello World! » affiché dans le programme ci-dessus est également une chaîne de caractères. Les valeurs numériques telles que 4 et 4.5 sont respectivement des nombres entiers et des nombres flottants. Vous pouvez transformer un nombre entier ou un nombre flottant en une chaîne de caractères, et vice versa en utilisant les fonctions intégrées __int()__, __float()__ et __str()__.\n\n![Image illustrant les fonctions str(), int() et float() de Python.](https://about.gitlab.com/images/blogimages/python7.png){: .shadow}\n\n## Le vocabulaire de Python\n\nPython est un des langages de programmation les plus simples. Il est facile à lire et à comprendre. Contrairement aux langages humains, Python possède un vocabulaire restreint, et des mots-clés réservés ayant une signification particulière. Les termes ne faisant pas partie de ce vocabulaire réservé n'ont de sens que pour vous et sont appelés variables. Ces 35 mots-clés réservés sont les suivants :\n\n![Les 35 mots-clés réservés à connaître pour apprendre Python](https://about.gitlab.com/images/blogimages/python8.png){: .shadow}\n\nVeillez à utiliser ces mots-clés dans leur but spécifique pour éviter d'induire l'interpréteur Python en erreur et de provoquer une erreur de syntaxe.\n\n### Nommer les variables\n\nVous aurez parfois besoin de stocker des valeurs dans votre code pour les récupérer plus tard, ce que vous pouvez faire en leur donnant des noms symboliques, appelés variables. Comme illustré ci-dessous, nous demandons à Python de stocker 5 et 6 avec les labels respectifs x et y, puis de les récupérer plus tard pour trouver leur somme. \n\n![Stockage des variables dans Python](https://about.gitlab.com/images/blogimages/python9.png){: .shadow}\n\nIl existe des règles pour choisir le nom d'une variable ; le non-respect de ces règles entraînera une erreur de syntaxe. \n\nVoici quelques règles obligatoires :\n- Le nom peut contenir des lettres et des chiffres, mais il ne peut pas commencer par un chiffre.\n- Un tiret bas peut être utilisé dans le nom pour séparer plusieurs mots.\n- Les symboles spéciaux comme @#$ sont interdits et ne doivent pas apparaître dans le nom.\n- Les mots-clés Python ne doivent pas être utilisés comme noms de variables.\n\n### Comprendre les opérateurs et les opérandes \n\nPython utilise des symboles spéciaux appelés « opérateurs » pour représenter les calculs mathématiques de base. Les valeurs auxquelles ces opérateurs sont appliqués sont appelées opérandes. Les symboles utilisés comme opérateurs pour la soustraction, l'addition, la division, la multiplication et l'exponentiation sont respectivement -, +, /, * et **.\n\n![Apprendre à coder en Python : symboles pour les opérateurs](https://about.gitlab.com/images/blogimages/python10.png){: .shadow}\n\nL'opérateur modulo (%) génère le reste du premier opérande divisé par le second opérande. Il est utile pour vérifier si un nombre est divisible par un autre et pour extraire le ou les chiffres les plus à droite d'un nombre.\n\n![Opérateur modulo](https://about.gitlab.com/images/blogimages/python11.png){: .shadow}\n\n### Utiliser les expressions\n\nUne combinaison de valeurs, de variables et d'opérateurs est appelée une expression. Une expression saisie dans le *shell* est évaluée et la réponse est affichée. Cependant, dans un script, une expression ne fait rien par elle-même.\n\nPython utilise la convention mathématique PEMDAS pour les opérateurs, ce qui signifie que P pour Parenthèses a la priorité la plus élevée, puis Exponentiation, Multiplication et Division, qui ont la même priorité. L'Addition et la Soustraction viennent ensuite et ont également la même priorité. Les opérateurs qui ont la même préférence sont également évalués de gauche à droite.\n\n![Apprendre python : PEMDAS](https://about.gitlab.com/images/blogimages/python12.png){: .shadow}\n\nLes opérateurs Addition et Multiplication fonctionnent aussi avec les chaînes de caractères pour la concaténation et la répétition d'une chaîne : \n\n![Opérateurs d'addition et de multiplication](https://about.gitlab.com/images/blogimages/python13.png){: .shadow}\n\nPython permet également d'obtenir la valeur d'une variable de l'utilisateur via son clavier. Cela peut être fait en utilisant une fonction intégrée appelée __input__.\n\n![Les bases de python : fonction input](https://about.gitlab.com/images/blogimages/python14.png){: .shadow}\n\n## Débutez sur Python en écrivant votre premier programme\n\nIl est maintenant temps d'écrire votre premier programme simple en utilisant tout ce que vous avez appris dans cet article. Écrivez un script qui prend deux nombres en entrée (input) et les additionne. Faites-le de vous-même, puis consultez le code ci-dessous pour comparer votre travail.\n\n![Écrire un premier programme sur Python](https://about.gitlab.com/images/blogimages/python15.png){: .shadow}\n\nFélicitations ! Vous venez d'écrire votre premier programme.\n\nApprendre Python est facile. Nous vous avons simplement aidé à acquérir les notions de base nécessaires pour maîtriser les bases de Python. C’est à vous de jouer maintenant ! \n\nPhoto par \u003Ca href=\"https://unsplash.com/@davidclode?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">David Clode\u003C/a> sur Unsplash.\n","devsecops",[760,9,712],"DevOps",{"slug":762,"featured":6,"template":691},"beginner-guide-python-programming","content:fr-fr:blog:beginner-guide-python-programming.yml","Beginner Guide Python Programming","fr-fr/blog/beginner-guide-python-programming.yml","fr-fr/blog/beginner-guide-python-programming",1,[667,696,722],1758326318818]