From d8d417d626cf7be673f687bcfd0d91680bd14bed Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Fri, 5 Apr 2024 16:18:52 +0200 Subject: [PATCH 1/9] [Project] Add missing files and improve project and repository hygiene - Add templates for issues and feature requests - Add common files like MAINTAINERS, CONTRIBUTING - Move files at root of project to dedicated folder Signed-off-by: Pierre-Yves Lapersonne --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83b1579..013e054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Diver] Missing execution permission for extract-emails-from-history.sh ([#171](https://github.com/Orange-OpenSource/floss-toolbox/issues/171)) - [Diver] Failed to process repositories at path with whitespaces ([#172](https://github.com/Orange-OpenSource/floss-toolbox/issues/172)) +### Added + +- Bug and feature request templates, and other files for the hygiene of the project + ## [2.20.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.20.0..2.19.0) - 2024-04-04 ### Added From 40360a812f1769c0c103f47146dda7f9f26b5e60 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Wed, 17 Apr 2024 18:08:17 +0200 Subject: [PATCH 2/9] [Licenses Inventory] Update to v4.0.6 (#161) * chore(#160): update of LicensesInventory (v4.0.6) Integration of the work done by my colleague Laurent Body (LicensesInventory 4.0.6). Tested-by: Pierre-Yves Lapersonne Reviewed-by: Pierre-Yves Lapersonne Co-authored-by: Laurent Body Co-authored-by: Pierre-Yves Lapersonne Signed-off-by: Pierre-Yves Lapersonne * chore(#160): add entry in CHANGELOG Signed-off-by: Pierre-Yves Lapersonne --------- Signed-off-by: Pierre-Yves Lapersonne Co-authored-by: Laurent Body --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 013e054..1a3e315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bug and feature request templates, and other files for the hygiene of the project +### Changed + +- [Licenses Inventory] Update to v4.0.6 ([#160](https://github.com/Orange-OpenSource/floss-toolbox/issues/160)) + ## [2.20.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.20.0..2.19.0) - 2024-04-04 ### Added From 6ada7502b718faa04d3d1229084ab045362fb09f Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Tue, 9 Jul 2024 11:15:44 +0200 Subject: [PATCH 3/9] Improve text generator for english emails - Fix typo in logs - Add .gitignore to exclude from versioning generated files - Add english template for newcomers on GitHub Signed-off-by: Pierre-Yves Lapersonne --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a3e315..86bfdca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- English template for email text generation about GitHub newcomers - Bug and feature request templates, and other files for the hygiene of the project ### Changed From 97d0b66e01eca0b0495e1937e053da2e6e6e3d17 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Mon, 16 Sep 2024 12:54:53 +0200 Subject: [PATCH 4/9] [#171] [#172] Permission and path with whitespaces fixes (#173) * fix: failed to process Git repository at path with whitespaces (#172) Closes #172 Signed-off-by: Pierre-Yves Lapersonne * fix: execution permission to Shell script missing (#171) Closes #171 Signed-off-by: Pierre-Yves Lapersonne --------- Signed-off-by: Pierre-Yves Lapersonne --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86bfdca..dc4948e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Licenses Inventory] Update to v4.0.6 ([#160](https://github.com/Orange-OpenSource/floss-toolbox/issues/160)) +### Fixed + +- [Diver] Missing execution permission for extract-emails-from-history.sh ([#171](https://github.com/Orange-OpenSource/floss-toolbox/issues/171)) +- [Diver] Failed to process repositories at path with whitespaces ([#172](https://github.com/Orange-OpenSource/floss-toolbox/issues/172)) + ## [2.20.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.20.0..2.19.0) - 2024-04-04 ### Added From 95f17905ea156da50887f19b521adbf594a529b6 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Mon, 16 Sep 2024 12:56:49 +0200 Subject: [PATCH 5/9] Prepare version 2.21.0 Signed-off-by: Pierre-Yves Lapersonne --- CHANGELOG.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4948e..83b1579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,20 +23,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Diver] Missing execution permission for extract-emails-from-history.sh ([#171](https://github.com/Orange-OpenSource/floss-toolbox/issues/171)) - [Diver] Failed to process repositories at path with whitespaces ([#172](https://github.com/Orange-OpenSource/floss-toolbox/issues/172)) -### Added - -- English template for email text generation about GitHub newcomers -- Bug and feature request templates, and other files for the hygiene of the project - -### Changed - -- [Licenses Inventory] Update to v4.0.6 ([#160](https://github.com/Orange-OpenSource/floss-toolbox/issues/160)) - -### Fixed - -- [Diver] Missing execution permission for extract-emails-from-history.sh ([#171](https://github.com/Orange-OpenSource/floss-toolbox/issues/171)) -- [Diver] Failed to process repositories at path with whitespaces ([#172](https://github.com/Orange-OpenSource/floss-toolbox/issues/172)) - ## [2.20.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.20.0..2.19.0) - 2024-04-04 ### Added From 8d76c6340475f2071ad64bdd43d788538d3d40cf Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Tue, 17 Sep 2024 12:24:48 +0200 Subject: [PATCH 6/9] doc: update `still maintained` badge for README Badge on README.md saying of the project is still maintained or not is buggy. In addition the year in use was 2023, and we are in 2024. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4feed9..f60e78d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Opened issues](https://img.shields.io/github/issues-raw/Orange-OpenSource/floss-toolbox?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/issues) [![Apache 2.0 license](https://img.shields.io/github/license/Orange-OpenSource/floss-toolbox?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/blob/dev/LICENSE.txt) [![Versions](https://img.shields.io/github/v/release/Orange-OpenSource/floss-toolbox?label=Last%20version&style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/releases) -[![Still maintained](https://img.shields.io/maintenance/yes/2023?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/issues?q=is%3Aissue+is%3Aclosed) +[![Still maintained](https://img.shields.io/maintenance/yes/2024?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/issues?q=is%3Aissue+is%3Aclosed) [![Code size](https://img.shields.io/github/languages/code-size/Orange-OpenSource/floss-toolbox?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox) [![Shell](https://img.shields.io/badge/-Shell-89e051?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/search?l=shell) From 7d8f28c7af80c8227579c6fb3ec371c31b194d6c Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Wed, 8 Jan 2025 16:21:11 +0100 Subject: [PATCH 7/9] feat: add some samples for files (#184) (#185) Signed-off-by: Pierre-Yves Lapersonne --- CHANGELOG.md | 4 + samples/AUTHORS.txt | 13 +++ samples/CHANGELOG.md | 35 +++++++ samples/CODE_OF_CONDUCT.md | 130 ++++++++++++++++++++++++ samples/CONTRIBUTING.md | 165 +++++++++++++++++++++++++++++++ samples/CONTRIBUTORS.txt | 21 ++++ samples/DEVELOP.md | 94 ++++++++++++++++++ samples/NOTICE.txt | 19 ++++ samples/README.md | 19 ++++ samples/THIRD_PARTY.md | 28 ++++++ samples/bug_report.yml | 95 ++++++++++++++++++ samples/feature_request.yml | 76 ++++++++++++++ samples/pull_request_template.md | 72 ++++++++++++++ 13 files changed, 771 insertions(+) create mode 100644 samples/AUTHORS.txt create mode 100644 samples/CHANGELOG.md create mode 100644 samples/CODE_OF_CONDUCT.md create mode 100644 samples/CONTRIBUTING.md create mode 100644 samples/CONTRIBUTORS.txt create mode 100644 samples/DEVELOP.md create mode 100644 samples/NOTICE.txt create mode 100644 samples/README.md create mode 100644 samples/THIRD_PARTY.md create mode 100644 samples/bug_report.yml create mode 100644 samples/feature_request.yml create mode 100644 samples/pull_request_template.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 83b1579..a2c6cfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.21.0..dev) +### Added + +- Samples for common files to add in projects ([#184](https://github.com/Orange-OpenSource/floss-toolbox/issues/184)) + ## [2.21.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.21.0..2.20.0) - 2024-09-164 ### Added diff --git a/samples/AUTHORS.txt b/samples/AUTHORS.txt new file mode 100644 index 0000000..e7c2a5f --- /dev/null +++ b/samples/AUTHORS.txt @@ -0,0 +1,13 @@ +# This is the official list of ouds-ios authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as one of +# Organization's name +# Individual's name +# Individual's name +# See CONTRIBUTORS for the meaning of multiple email addresses. + +# Please keep the list sorted. + +Orange SA diff --git a/samples/CHANGELOG.md b/samples/CHANGELOG.md new file mode 100644 index 0000000..4eac3da --- /dev/null +++ b/samples/CHANGELOG.md @@ -0,0 +1,35 @@ + + +# CHANGELOG + +All notable changes to this project will be documented in this file. +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## [Unreleased](project-url-pointinto-last-tag...default-branch) + + +### Added + +... + +### Changed + +... + +### Removed + +... + +### Fixed + +... + +### Security + +... diff --git a/samples/CODE_OF_CONDUCT.md b/samples/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..44e8049 --- /dev/null +++ b/samples/CODE_OF_CONDUCT.md @@ -0,0 +1,130 @@ + + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +julien.deramond@orange.com and opensource.contact@orange.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/samples/CONTRIBUTING.md b/samples/CONTRIBUTING.md new file mode 100644 index 0000000..0d94efb --- /dev/null +++ b/samples/CONTRIBUTING.md @@ -0,0 +1,165 @@ + + + +# Contributing to {project-name} + + +Looking to contribute something {project-name}? **Here's how you can help.** + +Please take a moment to review this document in order to make the contribution process easy for everyone involved. + +Following these guidelines helps to communicate that you respect the time of the developers managing and developing this Open Source project. +In return, they should reciprocate that respect in addressing your issue or assessing patches and features. + + +You should also have a look on the wiki if you are looking for information about how the project is managed, the releases and tests done, and how to do some actions. +[The wiki define the way to work anyone must agree]({project-wiki-url}). + + +[The Code of Conduct]({project-CoC-url}) defines how we should interact together. + +## Using the Issue Tracker + + +The [issue tracker]({project-issues-tracker-url}) is the preferred channel for [bug reports](#bug-reports), [feature requests](#feature-requests) and [submitting pull requests](#pull-requests), but please respect the following restrictions: + + +- Please **do not** use the issue tracker for personal support requests. [GitHub Discussions]({project-discussions-url}) or our internal Orange communication tools are better places to get help. + +- Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others. + +- Please **do not** post comments consisting solely of "+1" or ":thumbsup:". Use [GitHub's "reactions" feature](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) instead. We reserve the right to delete comments which violate this rule. + +## Issues and Labels + +Our bug tracker utilizes several labels to help organize and identify issues. Here's what they represent and how we use them: + +- `feature` - Issues asking for a new feature to be added, or an existing one to be extended or modified. New features require a minor version bump (e.g., `v1.0.0` to `v1.1.0`) or a major version bump if public API broken (e.g., `v1.0.0` to `v2.0.0`) +- `help wanted` - Issues we need or would love help from the community to resolve. + + +For a complete look at our labels, see the [project labels page]({project-issues-labels-url}). + +## Bug Reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are extremely helpful, so thanks! + +Guidelines for bug reports: + +1. **Use the GitHub issue search** — check if the issue has already been reported. + + +2. **Check if the issue has been fixed** — try to reproduce it using the latest `develop` in the repository. + +3. **Isolate the problem** — ideally create a reduced reproducible test case. + +A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report. What is your environment? What steps will reproduce the issue? What device(s) and OS experience the problem? Do other devices show the bug differently? What would you expect to be the outcome? All these details will help people to fix any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the browser/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + +## Feature Requests + +Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the project. It's up to _you_ to make a strong case to convince the project's developers of the merits of this feature. Please provide as much detail and context as possible. + +## Pull requests + +Good pull requests (patches, improvements, new features) are a fantastic help. They should remain focused in scope and avoid containing unrelated commits. + +**Please ask first** before embarking on any **significant** pull request (e.g. implementing features, refactoring code, porting to a different language), otherwise you risk spending a lot of time working on something that the project's developers might not want to merge into the project. For trivial things, or things that don't require a lot of your time, you can go ahead and make a PR. + +Please adhere to the [coding guidelines](#code-guidelines) used throughout the project (indentation, accurate comments, etc.) and any other requirements (such as test coverage). + +Adhering to the following process is the best way to get your work included in the project: + +1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork, and configure the remotes: + + + ```bash + # Clone your fork of the repo into the current directory (use either SSH or HTTPS) + git clone https://github.com//{project-name}.git + # Navigate to the newly cloned directory + cd {project-name} + # Assign the original repo to a remote called "upstream" (use either SSH or HTTPS) + git remote add upstream https://github.com/Orange-OpenSource/{project-name}.git + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout develop + git pull upstream develop + ``` + + +3. Create a new topic branch (off the `develop` project development branch) to contain your feature, change, or fix: + + ```bash + git checkout -b develop + ``` + +4. Commit your changes in logical chunks. Use Git's [interactive rebase](https://help.github.com/articles/about-git-rebase/) feature to tidy up your commits before making them public. Refer also to [commits style](#commits-style). + +5. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream develop + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + + +7. [Open a Pull Request](https://help.github.com/articles/about-pull-requests/) with a clear title and description against the `develop` branch. + + +**IMPORTANT**: By submitting a patch, you agree to allow the project owners to license your work under the terms of the [{project-license-name}]({project-license-url}). + +## Code Guidelines + +### Checking Coding Style + +Format your code before committing to ensure your changes follow our coding standards. + +## Commits style + +Try as best as possible to apply [conventional commits rules](https://www.conventionalcommits.org/en/v1.0.0/). +Keep in mind to have your commits well prefixed, and with the issue number between parenthesis at the end. +If your commits embed contributions for other people, do not forget to [add them as co-authors](https://docs.github.com/fr/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors). All of you should also comply to DCO. + +For example, given a commit to fix the issue n°43, the commit should be like: + +```text +fix: title of your commit (#43) + +Some details about the fix you propose + +Co-authored-by: First author firstname and lastname +Co-authored-by: Second author firstname and lastname + +Signed-off-by: First author firstname and lastname +Signed-off-by: Second author firstname and lastname +``` + +## License + + +By contributing your code, you agree to license your contribution under the [{project-license-name}]({project-license-url}). diff --git a/samples/CONTRIBUTORS.txt b/samples/CONTRIBUTORS.txt new file mode 100644 index 0000000..07fc66f --- /dev/null +++ b/samples/CONTRIBUTORS.txt @@ -0,0 +1,21 @@ +# This is the official list of people have contributed code to the +# {project-name}repository. +# +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Orange employees are listed here +# but not in AUTHORS, because Orange holds the copyright. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on who holds the copyright. +# +# Names should be added to this file like so: +# Individual's name +# Individual's name +# +# An entry with multiple email addresses specifies that the +# first address should be used in the submit logs and +# that the other addresses should be recognized as the +# same person. + +# Please keep the list sorted. diff --git a/samples/DEVELOP.md b/samples/DEVELOP.md new file mode 100644 index 0000000..45b1cdd --- /dev/null +++ b/samples/DEVELOP.md @@ -0,0 +1,94 @@ + + +# Developer guide + + +- [Technical preconditions](#technical-preconditions) +- [Build project](#build-project) +- [Run tests](#run-tests) +- [Developer Certificate of Origin](#developer-certificate-of-origin) +- [Commits, changelog, release note, versioning](#commits-changelog-release-note-versioning) + * [About commits](#about-commits) + * [About release note and changelog](#about-release-note-and-changelog) + +## Technical preconditions + + + + + +## Build project + + + +## Run tests + + + +## Developer Certificate of Origin + +The *Linux Foundation* *Developer Certificate of Origin* is applied for this project, and very commit must be signed-off. +You can get its full text at [developercertificate.org](https://developercertificate.org/). + +## Commits, changelog, release note, versioning + +### About commits + +#### Convention commits rules + +Try as best as possible to apply [conventional commits rules](https://www.conventionalcommits.org/en/v1.0.0/). +Keep in mind to have your commits well prefixed, and with the issue number between parenthesis at the end, and also if needed the pull request issue number. +If your commits embed contributions for other people, do not forget to [add them as co-authors](https://docs.github.com/fr/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors). +All of you should also comply to DCO. + +Your commit message should be prefixed by keywords [you can find in the specification](https://www.conventionalcommits.org/en/v1.0.0/#specification): +- `fix:` +- `feat:` +- `build:` +- `chore:` +- `ci:` +- `docs:` +- `style:` +- `refactor:` +- `perf:` +- `test:` + +You can add also ! after the keyword to say a breaking change occurs, and also add a scope between parenthesis like: +- `feat!:` breaking change because.. +- `feat(API)!:` breaking change in the API because.. +- `feat:` add something in the API... + +#### Chain of responsability + +We can add metafields picked from [this good guideline](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/submitting-patches.rst#n525) in the commit messages. + +This is not mandatory (yet) but a good practice and quite interesting to know who reviewed and validated what. + +For example, given a commit to fix the issue n°42, with Foo FOO and Bar BAR as commit authors, with Wizz WIZZ as source code reviewer, and John DOE as accessibility / PO / design reviewer, the commit should be like: + +```text +fix: title of your commit (#42) + +Some details about the fix you propose + +Co-authored-by: Foo FOO +Co-authored-by: Bar BAR + +Reviewed-by: Wizz WIZZ + +Acked-by: John DOE + +Signed-off-by: Foo FOO +Signed-off-by: Bar BAR +``` + +### About release note and changelog + +We try also to apply [keep a changelog](https://keepachangelog.com/en/1.0.0/), and [semantic versioning](https://semver.org/spec/v2.0.0.html) both with [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). + + \ No newline at end of file diff --git a/samples/NOTICE.txt b/samples/NOTICE.txt new file mode 100644 index 0000000..026edc7 --- /dev/null +++ b/samples/NOTICE.txt @@ -0,0 +1,19 @@ +Parts list under Orange SA Copyright +Copyright (C) Orange SA + +The following parts are proprietary information of Orange. +You shall not use or display any trade names, trademarks, service marks, products names, illustrations or designs used within the software +or displayed on this website and owned by Orange SA and its subsidiaries, +in whole or part of, in any medium, except as required for reasonable and customary use in describing the origin of the software +and reproducing the content of the NOTICE and DOCUMENTATION files. +Any use or displaying shall constitute an infringement under intellectual property laws of France and international conventions. + +The parts are: + +... + + + + + +End of the parts list under Orange SA Copyright diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000..d298d5d --- /dev/null +++ b/samples/README.md @@ -0,0 +1,19 @@ +# samples + +This directory contains several files samples you can use or pick and update for your own projects: + +1. **CONTRIBUTING.md**: To help newcomers and contributors to know how they can contribute +2. **AUTHORS.txt**: To list authors / copyright owners of the project (inspired by [OUDS iOS file](https://github.com/Orange-OpenSource/ouds-ios/blob/develop/.github/AUTHORS.txt)) +3. **CONTRIBUTORS.txt**: To list people who worked to the project (inspired by [OUDS iOS file](https://github.com/Orange-OpenSource/ouds-ios/blob/develop/.github/CONTRIBUTORS.txt)) +4. **CODE_OF_CONDUCT.md**: Contributor convenant / code of conduct for project +5. **DEVELOP.md**: Some rules explained to developers and details about environment and processes +6. **pull_request_template.md**: GitHub pull request template, to place in .github folder +7. **bug_report.yml**: GitHub issue template for bug report, here focused on mobile apps, to place in .github folder +8. **feature_request.yml**: GitHub issue template for feature request, to place in .github folder +9. **THIRD_PARTY.md**: File listing third-party elements +10. **NOTICE.txt**: If some files are for example proprietary but are still versionned and accessible (inspired by [OUDS iOS file](https://github.com/Orange-OpenSource/ouds-ios/blob/develop/NOTICE.txt)) +11. **CHANGELOG.md**: template for a CHANGELOG + +Some templates are missing because very dependant to the project, but here are some resources: + +1. About **[CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)** file diff --git a/samples/THIRD_PARTY.md b/samples/THIRD_PARTY.md new file mode 100644 index 0000000..523bc7e --- /dev/null +++ b/samples/THIRD_PARTY.md @@ -0,0 +1,28 @@ + + +# Third Party Softwares + +This document contains the list of Third Party Softwares along with the license information. + +Third Party Software may impose additional restrictions and it is the user's responsibility to ensure that they have met the licensing +requirements of the relevant license of the Third Party Software they are using. + + +For further details about versions, please refer to *Gemfile*, *Gemfile.lock*, *Package.swift*, *Package.resolved* and *Podfile* files. + + + + +## Dependency 1 + +Copyright years copyrightowners + +*Dependency 1* is distributed under the terms and conditions of the [Dependency 1 license](opensource.org URL). +You may download the source code on the [following website](Dependency 1 website /repo). diff --git a/samples/bug_report.yml b/samples/bug_report.yml new file mode 100644 index 0000000..c2cefbc --- /dev/null +++ b/samples/bug_report.yml @@ -0,0 +1,95 @@ +# Inspired by OUDS iOS file: https://github.com/Orange-OpenSource/ouds-ios/blob/develop/.github/ISSUE_TEMPLATE/bug_report.yml +# More focused for mobile apps + +name: Report a bug +description: Tell us about a bug or issue you may have identified in the app. +title: "[Bug] " +labels: [] # TODO: Update labels +assignees: [] # TODO: Update assignees +body: + - type: checkboxes + attributes: + label: Prerequisites + description: Take a couple minutes to help our maintainers work faster. + options: + - label: I have [searched the backlog]({project-issues-url}) for duplicate or closed issues # TODO: Update issues URL + required: true + - type: input + id: device + attributes: + label: Your test device + description: + placeholder: iPhone 13 pro, Samsung Galaxy S3 + validations: + required: true + - type: input + id: os-version + attributes: + label: OS Version + description: + placeholder: iOS 15.1, Android 4.4.2 + validations: + required: true + - type: input + id: app-version + attributes: + label: App version + placeholder: 1.2.3 + validations: + required: true + - type: input + id: app-build-number + attributes: + label: App build number / version number + placeholder: "13" + validations: + required: true + - type: input + id: app-build-type + attributes: + label: App build type + placeholder: alpha, beta, prod, DEBUG + validations: + required: true + - type: input + id: app-orientation + attributes: + label: App orientation + placeholder: Portrait and / or landscape + validations: + required: false + - type: input + id: device-language + attributes: + label: Device language + placeholder: Arabic + validations: + required: false + - type: input + id: app-language + attributes: + label: App language + placeholder: English + validations: + required: false + - type: textarea + id: what-happened + attributes: + label: Describe the issue + description: Provide a summary of the issue, including specific steps to reproduce with as much detail as possible. Do not forget to hide sensitive or personal data (phone numbers, accounts, messages, emails etc). + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: Provide a description of what you expected to happen. + validations: + required: true + - type: textarea + id: a11y-settings + attributes: + label: Accessibility settings + description: Provide a description about the accessibility settings you have (vocalization with Voice Over / Google Talkback, increased text sizes, keyboard navigation, high contrast, etc.) + validations: + required: true \ No newline at end of file diff --git a/samples/feature_request.yml b/samples/feature_request.yml new file mode 100644 index 0000000..400cb5e --- /dev/null +++ b/samples/feature_request.yml @@ -0,0 +1,76 @@ +# Inspired by OUDS iOS file: https://github.com/Orange-OpenSource/ouds-ios/blob/develop/.github/ISSUE_TEMPLATE/feature_request.yml +# More focused for mobile apps + +name: Feature Request +description: Suggest a new Epic to be added to the backlog. +title: "[Feature] " +labels: [] # TODO: Update labels +assignees: [] # TODO: Update assignees +body: + - type: checkboxes + attributes: + label: Prerequisites + description: Take a couple minutes to help our maintainers work faster. + options: + - label: I have [searched the backlog]({project-issues-url}) for duplicate or closed feature requests # TODO: Update issues URL + required: true + - type: markdown + attributes: + value: | + New Feature Description + - type: textarea + id: user + attributes: + label: As a + description: + placeholder: type of user / context of use + validations: + required: true + - type: textarea + id: need + attributes: + label: ... I want to + description: + placeholder: perform some task + validations: + required: true + - type: textarea + id: goal + attributes: + label: ... so that I can + description: + placeholder: achieve some goal + validations: + required: true + - type: textarea + id: motivation + attributes: + label: Motivation and context + description: Is your feature request related to a problem? Please describe. + placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: false + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: Describe the solution you'd like + placeholder: A clear and concise description of what you want to happen. + validations: + required: false + - type: textarea + id: other + attributes: + label: Other + description: Additional Context + placeholder: Add any other context about the problem here. + validations: + required: false + - type: input + id: contact + attributes: + label: Contact Details + description: How can we get in touch with you if we need more info? + placeholder: ex. email@example.com + validations: + required: false diff --git a/samples/pull_request_template.md b/samples/pull_request_template.md new file mode 100644 index 0000000..128a92b --- /dev/null +++ b/samples/pull_request_template.md @@ -0,0 +1,72 @@ + +_Note: Please transform `- [ ]` into `- (NA)` in the description when things are not applicable_ + +### Related issues + + + +### Description + + + +### Motivation & Context + + + +### Types of change + + + + +- Bug fix (non-breaking which fixes an issue) +- New feature (non-breaking change which adds functionality) +- Refactoring (non-breaking change) +- Breaking change (fix or feature that would change existing functionality) + +### Previews + + + +### Checklist + + + + + +#### Contribution + + +- [ ] I have read the [contributing guidelines]({project-contributing-file-url}) + +#### Accessibility + + +- [ ] My change follows [accessibility good practices](https://a11y-guidelines.orange.com/) + +#### Design + + +- [ ] My change respects the design guidelines of _Orange Unified Design System_ + +#### Development + + +- [ ] My change follows the [developer guide]({project-develop-file-url}) +- [ ] I checked my changes do not add new linter warnings +- [ ] I have added tests to cover my changes + +#### Documentation + +- [ ] My change introduces changes to the documentation and/or I have updated the documentation accordingly + +### Checklist (for Core Team only) + +- [ ] The evolution have been tested and the project builds +- [ ] Code review has been done by reviewers according to [CODEOWNERS file]({project-codeowners-file-url}) +- [ ] Design review has been done +- [ ] Accessibility review has been done +- [ ] Q/A team has tested the evolution +- [ ] Documentation has been updated if relevant +- [ ] Internal files have been updated if relevant (like CONTRIBUTING, DEVELOP, THIRD_PARTY, CONTRIBUTORS, NOTICE) +- [ ] CHANGELOG has been updated respecting [keep a changelog rules](https://keepachangelog.com/en/1.0.0/) and reference the issues +- [ ] [The wiki]({proejct-wiki-url}) has been updated if needed From b9a8cbe617ceeab7d6367e59b8085994a7ccad76 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Mon, 27 Jan 2025 13:03:48 +0100 Subject: [PATCH 8/9] feat: year review for GitHub and GitLab (#181) (#186) Add scripts in Python to request GitHub and GitLab API to compute some figures and metrics for a given year. Assisted-by: GPT-4o-mini (Dinootoo) Signed-off-by: Pierre-Yves Lapersonne --- .gitignore | 6 + CHANGELOG.md | 1 + LICENSES/BSD-3-Clause.txt | 11 + THIRD-PARTY.md | 9 + toolbox/github/README.md | 35 +- toolbox/github/dry-run.sh | 6 +- toolbox/github/github-year-review.py | 477 +++++++++++++++++++++++++++ toolbox/github/requirements.txt | 1 + toolbox/gitlab/README.md | 26 +- toolbox/gitlab/dry-run.sh | 6 +- toolbox/gitlab/gitlab-year-review.py | 191 +++++++++++ toolbox/gitlab/requirements.txt | 1 + 12 files changed, 766 insertions(+), 4 deletions(-) create mode 100644 LICENSES/BSD-3-Clause.txt create mode 100755 toolbox/github/github-year-review.py create mode 100644 toolbox/github/requirements.txt create mode 100755 toolbox/gitlab/gitlab-year-review.py create mode 100644 toolbox/gitlab/requirements.txt diff --git a/.gitignore b/.gitignore index 86c4fad..c09f8f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,19 @@ toolbox/github/data +toolbox/github/.env + toolbox/diver/data toolbox/diver/.floss-toolbox/data + toolbox/gitlab/data +toolbox/gitlab/.env + toolbox/utils/text-generator/_templates/new-GitHub-repository-contributors.fr.template.txt.result toolbox/utils/third-party-generator/components.csv.result toolbox/utils/third-party-generator/THIRD-PARTY.md.result toolbox/utils/third-party-generator/__pycache__ toolbox/utils/_/__pycache__/ toolbox/utils/reuse/.reuse/ + toolbox/LicensesInventory/.pytest_cache toolbox/LicensesInventory/sources/__pycache__ toolbox/LicensesInventory/sources/*/__pycache__ diff --git a/CHANGELOG.md b/CHANGELOG.md index a2c6cfa..650d735 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- GitHub and GitLab year review ([#181](https://github.com/Orange-OpenSource/floss-toolbox/issues/181)) - Samples for common files to add in projects ([#184](https://github.com/Orange-OpenSource/floss-toolbox/issues/184)) ## [2.21.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.21.0..2.20.0) - 2024-09-164 diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..fe354c5 --- /dev/null +++ b/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,11 @@ +Copyright + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/THIRD-PARTY.md b/THIRD-PARTY.md index c9dd4ff..84a2410 100644 --- a/THIRD-PARTY.md +++ b/THIRD-PARTY.md @@ -27,6 +27,15 @@ Copyright (c) 2009-2017 Wynn Netherland, Adam Stacoviak, Erik Michaels-Ober *octokit.rb* is distributed under the terms and conditions of the [MIT License](https://opensource.org/license/MIT). You may download the source code on the [following website](https://github.com/octokit/octokit.rb). +### python-dotenv + +Version 1.0.1 + +Copyright (c) 2014, Saurabh Kumar (python-dotenv), 2013, Ted Tieken (django-dotenv-rw), 2013, Jacob Kaplan-Moss (django-dotenv) + +*python-dotenv* is distributed under the terms and conditions of the [BSD 3-Clause License](https://opensource.org/license/BSD-3-clause). +You may download the source code on the [following website](https://github.com/theskumar/python-dotenv). + ### Ruby Git Version 1.18.0 diff --git a/toolbox/github/README.md b/toolbox/github/README.md index 70808b9..4de5fc1 100644 --- a/toolbox/github/README.md +++ b/toolbox/github/README.md @@ -19,6 +19,7 @@ Table of Contents * [Get repositories which seems to be empty or have not enough files](#get-repositories-which-seems-to-be-empty-or-have-not-enough-files) * [Define users permissions for all projects to "push"](#define-users-permissions-for-all-projects-to-push) * [Define teams permissions for all projects to "push"](#define-teams-permissions-for-all-projects-to-push) + * [Make a year review of the GitHub organization](#make-a-year-review-of-the-github-organization) * [Play with GitHub CLI (GH)](#play-with-github-cli-gh) * [Prerequisites](#prerequisites-1) * [Prepare project](#prepare-project-1) @@ -208,6 +209,38 @@ Permissions will be set to "read". bash GitHubWizard.sh set-teams-permissions-to-read ``` +### Make a year review of the GitHub organization + +_Keywords: #organisation #GitHub #KPI #year #review_ + +You will need to define a *.env* file with the GitHub API token for key *GITHUB_API_TOKEN*, the organization name and some settings. +Here the organization name is *Orange-OpenSource*, replace with your own and add the suitable token. + +See for example: +```text +GITHUB_API_TOKEN=your-token +ORGANIZATION_NAME=Orange-OpenSource +TOP_N_PROG_LANG=5 +TOP_N_LEAST_PROG_LANG=5 +TOP_N_LICENSES=5 +TOP_N_CONTRIBUTORS_OVERALL=10 +TOP_N_CONTRIBUTORS_FOR_YEAR=10 +TOP_N_REPOS_MOST_COMMITS=5 +``` + +Run the following command to compute a year review of the organization + +```shell +# Do not forget to install dependencies +pip install -r requirements.txt + +# For year 2024 +python3.8 github-year-review.py --year 2024 + +# For year 20°24 and commits counts computing (can be time expansive) +python3.8 github-year-review.py --year 2024 --count-commits +``` + # Play with GitHub CLI (GH) ## Prerequisites @@ -304,4 +337,4 @@ brew install gitleaks You need to define in the _configuration.rb_ files the Github organisation at **GITHUB_ORGANIZATION_NAME** and also your GitHub personal token at **GITHUB_PERSONAL_ACCESS_TOKEN**. -**You should also have your _git_ environment ready i.e. add your SSH private key if you clone by SSH for example. _gh_ must be installed, and _python3_ be ready. Obviously _gitleaks_ must be installed** \ No newline at end of file +**You should also have your _git_ environment ready i.e. add your SSH private key if you clone by SSH for example. _gh_ must be installed, and _python3_ be ready. Obviously _gitleaks_ must be installed** diff --git a/toolbox/github/dry-run.sh b/toolbox/github/dry-run.sh index b2a19e0..a076694 100755 --- a/toolbox/github/dry-run.sh +++ b/toolbox/github/dry-run.sh @@ -12,7 +12,7 @@ # Since...............: 10/03/2023 # Description.........: Make a dry-run of the github module to check if everything is ready to use -# Version.............: 1.2.0 +# Version.............: 1.3.0 set -eu @@ -108,6 +108,10 @@ CheckIfFileExists "./utils/GitHubWrapper.rb" CheckIfFileExists "./utils/GitWrapper.rb" CheckIfFileExists "./utils/IO.rb" +CheckIfFileExists "./github-year-review.py" +CheckIfFileExists "./requirements.txt" +CheckIfFileExists "./.env" # Warning: not versioned but mandatory for Python script above + # Runtimes and tools # ------------------ diff --git a/toolbox/github/github-year-review.py b/toolbox/github/github-year-review.py new file mode 100755 index 0000000..a46bec0 --- /dev/null +++ b/toolbox/github/github-year-review.py @@ -0,0 +1,477 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Software Name: floss-toolbox +# SPDX-FileCopyrightText: Copyright (c) Orange SA +# SPDX-License-Identifier: Apache-2.0 +# +# This software is distributed under the Apache 2.0 license, +# the text of which is available at https://opensource.org/license/apache-2-0 +# or see the "LICENSE.txt" file for more details. +# +# Authors: See CONTRIBUTORS.txt +# Software description: A toolbox of scripts to help work of forges admins and open source referents + +import argparse +from collections import Counter, defaultdict +from datetime import datetime +from dotenv import load_dotenv +import os +import requests +import sys +import time + +# Configuration - Tool +# -------------------- + +VERSION = "1.0.0" + +ERROR_BAD_PREREQUISITES = 1 + +# Configuration - Load +# -------------------- + +load_dotenv() + +# Configuration - GitHub +# ---------------------- + +# To create the GitHub Personal Access Token for the organization: https://github.com/settings/personal-access-tokens +GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN") +# Organization to scan +ORGANIZATION_NAME = os.getenv("ORGANIZATION_NAME") + +# Configuration - Tunning +# ----------------------- + +# Number of top programming languages in use to extract +TOP_N_PROG_LANG = os.getenv("TOP_N_PROG_LANG") +# Number of programming languages the least used to extract +TOP_N_LEAST_PROG_LANG = os.getenv("TOP_N_LEAST_PROG_LANG") +# Number of top licenses in use to extract +TOP_N_LICENSES = os.getenv("TOP_N_LICENSES") +# Number of top contributors over all years +TOP_N_CONTRIBUTORS_OVERALL = os.getenv("TOP_N_CONTRIBUTORS_OVERALL") +# Number of top contributors over the specified year +TOP_N_CONTRIBUTORS_FOR_YEAR = os.getenv("TOP_N_CONTRIBUTORS_FOR_YEAR") +# Number of top repositories with the highest commits +TOP_N_REPOS_MOST_COMMITS = os.getenv("TOP_N_REPOS_MOST_COMMITS") + +# Configuration - Endpoints +# ------------------------- + +GITHUB_API_URL = f"https://api.github.com/orgs/{ORGANIZATION_NAME}/repos" +ORG_MEMBERS_URL = f"https://api.github.com/orgs/{ORGANIZATION_NAME}/members" +OUTSIDE_COLLABORATORS_URL = f"https://api.github.com/orgs/{ORGANIZATION_NAME}/outside_collaborators" + +HEADERS = { + "Accept": "application/vnd.github.v3+json", + "Authorization": f"token {GITHUB_API_TOKEN}" +} + +# Services +# -------- + +def check_prerequisites(): + """Check if all configuration elements have been laoded through .env file. + If not, exists. + """ + if GITHUB_API_TOKEN is None: + print("❌ Error: GitHub token is not defined. Please set the GITHUB_API_TOKEN environment variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if ORGANIZATION_NAME is None or ORGANIZATION_NAME.strip() == "": + print("❌ Error: Organization name is not defined. Please set the ORGANIZATION_NAME variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if TOP_N_PROG_LANG is None or TOP_N_PROG_LANG.strip() == "": + print("❌ Error: TOP_N_PROG_LANG is not defined. Please set the TOP_N_PROG_LANG variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if TOP_N_LEAST_PROG_LANG is None or TOP_N_LEAST_PROG_LANG.strip() == "": + print("❌ Error: TOP_N_LEAST_PROG_LANG name is not defined. Please set the TOP_N_LEAST_PROG_LANG variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if TOP_N_LICENSES is None or TOP_N_LICENSES.strip() == "": + print("❌ Error: TOP_N_LICENSES name is not defined. Please set the TOP_N_LICENSES variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if TOP_N_CONTRIBUTORS_OVERALL is None or TOP_N_CONTRIBUTORS_OVERALL.strip() == "": + print("❌ Error: TOP_N_CONTRIBUTORS_OVERALL name is not defined. Please set the TOP_N_CONTRIBUTORS_OVERALL variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if TOP_N_CONTRIBUTORS_FOR_YEAR is None or TOP_N_CONTRIBUTORS_FOR_YEAR.strip() == "": + print("❌ Error: TOP_N_CONTRIBUTORS_FOR_YEAR name is not defined. Please set the TOP_N_CONTRIBUTORS_FOR_YEAR variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if TOP_N_REPOS_MOST_COMMITS is None or TOP_N_REPOS_MOST_COMMITS.strip() == "": + print("❌ Error: TOP_N_REPOS_MOST_COMMITS name is not defined. Please set the TOP_N_REPOS_MOST_COMMITS variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + +def get_repositories(): + """Fetch all repositories for the organization. + + Returns: + list: A list of repository dictionaries. + """ + print("🔨 Fetching repositories...") + repos = [] + page = 1 + while True: + response = requests.get(f"{GITHUB_API_URL}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch repositories, status code:", response.status_code) + break # Troubles with GitHub API maybe, exit + data = response.json() + if not data: + print("🔨 No more repositories to fetch.") + break # No more data, exit. + repos.extend(data) + print(f"🔨 Fetched {len(data)} repositories from page {page}.") + page += 1 + print("🔨 Total repositories fetched:", len(repos)) + return repos + +def get_commits_count(repo_full_name, year): + """Fetch the number of commits for a given repository within a specific year. + + Parameters: + repo_full_name (str): The full name of the repository (e.g., "owner/repo"). + year (int): The year to filter commits. + + Returns: + int: The number of commits in the specified year. + """ + print(f"🔨 Fetching commits for repository: {repo_full_name}...") + commits_url = f"https://api.github.com/repos/{repo_full_name}/commits" + commits_count = 0 + page = 1 + + # Define the date range for the specified year + since = f"{year}-01-01T00:00:00Z" + until = f"{year + 1}-01-01T00:00:00Z" + + while True: + response = requests.get(f"{commits_url}?page={page}&per_page=100&since={since}&until={until}", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch commits, status code:", response.status_code) + break # Troubles with GitHub API maybe, exit + data = response.json() + if not data: + print("🔨 No more commits to fetch for repository:", repo_full_name) + break # No more data, exit + commits_count += len(data) + print(f"🔨 Fetched {len(data)} commits from page {page}.") + page += 1 + print(f"🔨 Total commits fetched for repository {repo_full_name}: {commits_count}") + return commits_count + +def get_members(): + """Fetch all members of the organization. + + Returns: + list: A list of member dictionaries. + """ + print("🔨 Fetching organization members...") + members = [] + page = 1 + while True: + response = requests.get(f"{ORG_MEMBERS_URL}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch members, status code:", response.status_code) + break # Troubles with GitHub API maybe, exit + data = response.json() + if not data: + print("🔨 No more members to fetch.") + break # No more data, exit + members.extend(data) # Add the fetched members to the list + print(f"🔨 Fetched {len(data)} members from page {page}.") + page += 1 # Move to the next page + print("🔨 Total members fetched:", len(members)) + return members + +def get_outside_collaborators(): + """Fetch all outside collaborators of the organization. + + Returns: + list: A list of outside collaborator dictionaries. + """ + print("🔨 Fetching outside collaborators...") + collaborators = [] + page = 1 + while True: + response = requests.get(f"{OUTSIDE_COLLABORATORS_URL}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch outside collaborators, status code:", response.status_code) + break # Exit loop if the response is not successful + data = response.json() + if not data: + print("🔨 No more outside collaborators to fetch.") + break # Exit loop if no more data is returned + collaborators.extend(data) + print(f"🔨 Fetched {len(data)} outside collaborators from page {page}.") + page += 1 + print("🔨 Total outside collaborators fetched:", len(collaborators)) + return collaborators + +def analyze_repositories(repos, year, count_commits): + """ + Analyze repositories to gather various statistics. + + Parameters: + - repos (list): A list of repository dictionaries fetched from the GitHub API. + - year (int): The year for which to analyze contributions (e.g., 2024). + - count_commits (bool): A flag indicating whether to count commits in the analysis. + + Returns: + dict: A dictionary containing various statistics, including: + - total_repos (int): Total number of repositories. + - archived_repos (int): Number of archived repositories. + - forked_repos (int): Number of forked repositories. + - non_forked_repos (int): Number of non-forked repositories. + - total_forks (int): Total number of forks across all repositories. + - most_stars_repo (dict): Repository with the most stars. + - most_forks_repo (dict): Repository with the most forks. + - top_languages (list): List of the top programming languages used. + - total_lines (dict): Total lines of code for the top programming languages. + - top_licenses (list): List of the top licenses used in repositories. + - year_repos (int): Number of repositories created in the specified year. + - organization_forks_year (int): Number of forks created by the organization the given year. + - total_commits (int): Total number of commits across all repositories. + - top_repos (list): Top 3 repositories by commits. + - top_contributors_overall (list): Top 5 contributors overall. + - top_contributors_yearly (list): Top 10 contributors for the specified year. + - least_used_languages (list): 3 least used programming languages. + - largest_projects (dict): Largest project for each programming language. + """ + print("🔨 Analyzing repositories...") + total_repos = len(repos) # Total number of repositories + archived_repos = sum(1 for repo in repos if repo['archived']) # Count archived repositories + forked_repos = sum(1 for repo in repos if repo['fork']) # Count forked repositories + non_forked_repos = total_repos - forked_repos # Count non-forked repositories + + # Total forks count from all repositories + total_forks = sum(repo['forks_count'] for repo in repos) + + # Number of forks created the given year + organization_forks_year = sum(1 for repo in repos if repo['fork'] and datetime.strptime(repo['created_at'], '%Y-%m-%dT%H:%M:%SZ').year == year) + + # Stars count and identifying the most starred and forked repositories + stars_count = sum(repo['stargazers_count'] for repo in repos) + most_stars_repo = max(repos, key=lambda r: r['stargazers_count'], default=None) + most_forks_repo = max(repos, key=lambda r: r['forks_count'], default=None) + + # Count programming languages used in the repositories + languages = Counter() + largest_projects = {} # To track the largest project for each language + total_contributor_commits = defaultdict(int) # Total contributions + yearly_contributor_commits = defaultdict(int) # Contributions for the specified year + + for repo in repos: + if repo['language'] and not repo['fork'] and not repo['archived']: # Exclude forks and archived repos + languages[repo['language']] += 1 + # Track the largest project for each language + if repo['language'] not in largest_projects or repo['size'] > largest_projects[repo['language']]['size']: + largest_projects[repo['language']] = {'name': repo['full_name'], 'size': repo['size']} + + # Get the top 5 languages used + top_languages = languages.most_common(int(TOP_N_PROG_LANG)) + total_lines = {lang: 0 for lang, _ in top_languages} + + # Estimate total lines of code for top languages + for repo in repos: + if repo['language'] in total_lines and not repo['fork'] and not repo['archived']: + total_lines[repo['language']] += repo['size'] + + # Count repositories created in a specific year + year_repos = sum(1 for repo in repos if datetime.strptime(repo['created_at'], '%Y-%m-%dT%H:%M:%SZ').year == year) + + # Count forks created by the organization (i.e., forks of other repositories) + organization_forks = sum(1 for repo in repos if repo['fork']) + + # Count licenses used in the repositories + licenses = Counter(repo['license']['name'] for repo in repos if repo['license']) + top_licenses = licenses.most_common(int(TOP_N_LICENSES)) + + # Calculate total commits across all repositories if enabled + total_commits = 0 + commits_per_repo = {} + + if count_commits: + for index, repo in enumerate(repos, start=1): + # Only count commits for non-forked and non-archived repositories + # Some forks are for exmaple from linux project, to much noise in the data + if not repo['fork'] and not repo['archived']: + # Get commits for the specified year + commits_count = get_commits_count(repo['full_name'], year) + total_commits += commits_count + commits_per_repo[repo['full_name']] = commits_count + + # Get all commits for total contributor calculation + all_commits_url = f"https://api.github.com/repos/{repo['full_name']}/commits" + page = 1 + while True: + response = requests.get(f"{all_commits_url}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + break + commits_data = response.json() + if not commits_data: + break + for commit in commits_data: + author = commit['commit']['author']['name'] + total_contributor_commits[author] += 1 # Total contributions + # Check if the commit is in the specified year + commit_date = commit['commit']['author']['date'] + if year == datetime.strptime(commit_date, '%Y-%m-%dT%H:%M:%SZ').year: + yearly_contributor_commits[author] += 1 # Contributions for the specified year + page += 1 + print(f"🔨 Analyzing repository {index}/{total_repos}") + + # Get the top N contributors overall + top_contributors_overall = sorted(total_contributor_commits.items(), key=lambda x: x[1], reverse=True)[:int(TOP_N_CONTRIBUTORS_OVERALL)] + + # Get the top N contributors for the specified year + top_contributors_yearly = sorted(yearly_contributor_commits.items(), key=lambda x: x[1], reverse=True)[:int(TOP_N_CONTRIBUTORS_FOR_YEAR)] + + # Get the top N repositories with the most commits + top_repos = sorted(commits_per_repo.items(), key=lambda x: x[1], reverse=True)[:int(TOP_N_REPOS_MOST_COMMITS)] + + # Get the N least used programming languages + least_used_languages = sorted(languages.items(), key=lambda x: x[1])[:int(TOP_N_LEAST_PROG_LANG)] + + print("🔨 Analysis complete.") + return { + "total_repos": total_repos, + "archived_repos": archived_repos, + "forked_repos": forked_repos, + "non_forked_repos": non_forked_repos, + "total_forks": total_forks, + "most_stars_repo": most_stars_repo, + "most_forks_repo": most_forks_repo, + "top_languages": top_languages, + "total_lines": total_lines, + "top_licenses": top_licenses, + "year_repos": year_repos, + "organization_forks_year": organization_forks_year, + "total_commits": total_commits, + "top_repos": top_repos, + "top_contributors_overall": top_contributors_overall, + "top_contributors_yearly": top_contributors_yearly, + "least_used_languages": least_used_languages, + "largest_projects": largest_projects + } + +def get_total_members(): + """Get the total number of members including outside collaborators. + + Returns: + int: Total number of members in the organization. + """ + print("🔨 Fetching total members...") + members = get_members() + outside_collaborators = get_outside_collaborators() + total = len(members) + len(outside_collaborators) + print("🔨 Total members including outside collaborators:", total) + return total + +def estimate_private_members(total_members, visible_members): + """Estimate the number of private members in the organization. + + Parameters: + total_members (int): Total number of members including outside collaborators. + visible_members (int): Number of visible members. + + Returns: + int: Estimated number of private members. + """ + private_members = total_members - visible_members + print("🔨 Estimated private members:", private_members) + return private_members + +# Services - Main +# --------------- + +def main(): + """Main function to execute the analysis.""" + + check_prerequisites() + + # Parse command-line arguments + parser = argparse.ArgumentParser(description=f"Analyze GitHub organization repositories (Version: {VERSION}).") + parser.add_argument("--year", type=int, required=True, help="The year to analyze (e.g., 2024).") + parser.add_argument("--count-commits", action='store_true', help="Enable commit counting in the analysis.") + args = parser.parse_args() + + start_time = time.time() + + repos = get_repositories() + total_members = get_total_members() + visible_members = len(get_members()) + + # Analyze repositories for the specified year + analysis = analyze_repositories(repos, args.year, args.count_commits) + + # Estimate the number of private members + private_members_count = estimate_private_members(total_members, visible_members) + + # Calculate elapsed time + elapsed_time = time.time() - start_time + + # Print the analysis results + print("\n-----------------------") + print("Analysis results below:\n") + print("👉 Total repositories:", analysis["total_repos"], "\n") + print("👉 Archived repositories:", analysis["archived_repos"], "\n") + print("👉 Forked repositories:", analysis["forked_repos"], "\n") + print("👉 Non-forked repositories:", analysis["non_forked_repos"], "\n") + print("👉 Total forks:", analysis["total_forks"], "\n") + print("👉 Most stars repository:", analysis["most_stars_repo"]["name"] if analysis["most_stars_repo"] else "N/A", "\n") + print("👉 Most forks repository:", analysis["most_forks_repo"]["name"] if analysis["most_forks_repo"] else "N/A", "\n") + print("👉 Top languages:", analysis["top_languages"], "\n") + print("👉 Total lines of code for topl anguages:", analysis["total_lines"], "\n") + print("👉 Top licenses:", analysis["top_licenses"], "\n") + print("👉 Total members in organization (including outside collaborators):", total_members, "\n") + print("👉 Visible members:", visible_members, "\n") + print("👉 Estimated private members:", private_members_count, "\n") + print("👉 Repositories created in", args.year, ":", analysis["year_repos"], "\n") + print("👉 Forks created by the organization in", args.year, ":", analysis["organization_forks_year"], "\n") + + if args.count_commits: + print("👉 Total commits across all repositories:", analysis["total_commits"]) + + # Print top repositories by commits + print(f"\n💪 Top {TOP_N_REPOS_MOST_COMMITS} repositories by commits:") + for repo, commits in analysis["top_repos"]: + print(f"\t{repo}: {commits} commits") + + # Print top 5 contributors overall + print(f"\n💪 Top {TOP_N_CONTRIBUTORS_OVERALL} contributors overall:") + for contributor, commits in analysis["top_contributors_overall"]: + print(f"\t{contributor}: {commits} commits") + + # Print top 10 contributors for the specified year + print(f"\n💪 Top {TOP_N_CONTRIBUTORS_FOR_YEAR} contributors for the year:") + for contributor, commits in analysis["top_contributors_yearly"]: + print(f"\t{contributor}: {commits} commits") + print(f"\n") + + # Print the 3 least used programming languages + print(f"💪 {TOP_N_LEAST_PROG_LANG} least used programming languages:") + for lang, count in analysis["least_used_languages"]: + print(f"\t{lang}: {count} repositories") + + # Print the largest project for each of the top programming languages + print("\n💪 Largest project for each top programming language:") + for lang, _ in analysis["top_languages"]: + if lang in analysis["largest_projects"]: + largest_project = analysis["largest_projects"][lang] + print(f"\t{lang}: {largest_project['name']} (Size: {largest_project['size']} KB)") + else: + print(f"\t{lang}: Only forks available.") + + print(f"\n⌛ Elapsed Time: {elapsed_time:.2f} seconds") # Print elapsed time + +# Services - Run +# -------------- + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/toolbox/github/requirements.txt b/toolbox/github/requirements.txt new file mode 100644 index 0000000..3f8fa27 --- /dev/null +++ b/toolbox/github/requirements.txt @@ -0,0 +1 @@ +python-dotenv==1.0.1 \ No newline at end of file diff --git a/toolbox/gitlab/README.md b/toolbox/gitlab/README.md index e4e365f..fdd2865 100644 --- a/toolbox/gitlab/README.md +++ b/toolbox/gitlab/README.md @@ -7,6 +7,7 @@ Table of Contents * [Features](#features) * [Make a backup of organization repositories](#make-a-backup-of-organization-repositories) * [Check if there are leaks in organisation repositories (using gitleaks)](#check-if-there-are-leaks-in-organisation-repositories-using-gitleaks) + * [Make a year review of the GitLab organization](#make-a-year-review-of-the-gitlab-organization) # Play with GitLab web API @@ -75,4 +76,27 @@ brew install gitleaks You need to define in the _configuration.rb_ files the GitLab organisation ID at **GITLAB_ORGANIZATION_ID**. You have to also define the location to store clones at **REPOSITORIES_CLONE_LOCATION_PATH** and the access token at **GILAB_PERSONAL_ACCESS_TOKEN**. -**You should also have your _git_ environment ready i.e. add your SSH private key if you clone by SSH for example. _gh_ must be installed, and _python3_ be ready. Obviously _gitleaks_ must be installed** \ No newline at end of file +**You should also have your _git_ environment ready i.e. add your SSH private key if you clone by SSH for example. _gh_ must be installed, and _python3_ be ready. Obviously _gitleaks_ must be installed** + +### Make a year review of the GitLab organization + +_Keywords: #organisation #GitLab #KPI #year #review_ + +You will need to define a *.env* file with the GitLab API token for key *GITLAB_API_TOKEN*, the organization name and some settings. +Here the organization name is *Orange-OpenSource*, replace with your own and add the suitable token. + +See for example: +```text +GITLAB_API_TOKEN=your-api-token +ORGANIZATION_NAME=Orange-OpenSource +``` + +Run the following command to compute a year review of the organization + +```shell +# Do not forget to install dependencies +pip install -r requirements.txt + +# For year 2024 +python3.8 gitlab-year-review.py --year 2024 +``` \ No newline at end of file diff --git a/toolbox/gitlab/dry-run.sh b/toolbox/gitlab/dry-run.sh index a2291ce..fa4a789 100755 --- a/toolbox/gitlab/dry-run.sh +++ b/toolbox/gitlab/dry-run.sh @@ -12,7 +12,7 @@ # Since...............: 10/03/2023 # Description.........: Make a dry-run of the gitlab module to check if everything is ready to use -# Version.............: 1.1.0 +# Version.............: 1.2.0 set -eu @@ -97,6 +97,10 @@ CheckIfFileExists "./utils/dump-git-repositories-from-gitlab.sh" CheckIfFileExists "../github/utils/extract-repos-field-from-json.py" # Stored in github folder but used by dump-git-repositories-from-gitlab.sh CheckIfFileExists "../github/utils/count-leaks-nodes.py" # Stored in github folder but used by check-leaks-from-gitlab.sh +CheckIfFileExists "./gitlab-year-review.py" +CheckIfFileExists "./requirements.txt" +CheckIfFileExists "./.env" # Warning: not versioned but mandatory for Python script above + # Runtimes and tools # ------------------ diff --git a/toolbox/gitlab/gitlab-year-review.py b/toolbox/gitlab/gitlab-year-review.py new file mode 100755 index 0000000..3d61f46 --- /dev/null +++ b/toolbox/gitlab/gitlab-year-review.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Software Name: floss-toolbox +# SPDX-FileCopyrightText: Copyright (c) Orange SA +# SPDX-License-Identifier: Apache-2.0 + +import argparse +from datetime import datetime +from dotenv import load_dotenv +import os +import requests +import sys +import time + +# Configuration - Tool +# -------------------- + +VERSION = "1.0.0" + +ERROR_BAD_PREREQUISITES = 1 + +# Configuration - Load +# -------------------- + +load_dotenv() + +# Configuration - GitLab +# ---------------------- + +GITLAB_API_TOKEN = os.getenv("GITLAB_API_TOKEN") +ORGANIZATION_NAME = os.getenv("ORGANIZATION_NAME") + +# Configuration - Endpoints +# ------------------------- + +GROUPS_URL = f"https://gitlab.com/api/v4/groups/{ORGANIZATION_NAME}/subgroups" +PROJECTS_URL = f"https://gitlab.com/api/v4/groups/{{group_id}}/projects" +ORG_MEMBERS_URL = f"https://gitlab.com/api/v4/groups/{ORGANIZATION_NAME}/members" + +HEADERS = { + "Private-Token": GITLAB_API_TOKEN +} + +# Services +# -------- + +def check_prerequisites(): + """Check if all configuration elements have been loaded through .env file.""" + if GITLAB_API_TOKEN is None: + print("❌ Error: GitLab token is not defined. Please set the GITLAB_API_TOKEN environment variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + + if ORGANIZATION_NAME is None or ORGANIZATION_NAME.strip() == "": + print("❌ Error: Organization name is not defined. Please set the ORGANIZATION_NAME variable.") + sys.exit(ERROR_BAD_PREREQUISITES) + +def get_subgroups(): + """Fetch all subgroups for the organization.""" + print("🔨 Fetching subgroups...") + subgroups = [] + page = 1 + while True: + response = requests.get(f"{GROUPS_URL}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch subgroups, status code:", response.status_code) + break + data = response.json() + if not data: + print("🔨 No more subgroups to fetch.") + break + subgroups.extend(data) + print(f"🔨 Fetched {len(data)} subgroups from page {page}.") + page += 1 + print("🔨 Total subgroups fetched:", len(subgroups)) + return subgroups + +def get_projects(group_id): + """Fetch all projects for a given subgroup.""" + print(f"🔨 Fetching projects for group ID: {group_id}...") + projects = [] + page = 1 + while True: + response = requests.get(f"{PROJECTS_URL.format(group_id=group_id)}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch projects, status code:", response.status_code) + break + data = response.json() + if not data: + print("🔨 No more projects to fetch.") + break + projects.extend(data) + print(f"🔨 Fetched {len(data)} projects from page {page}.") + page += 1 + print("🔨 Total projects fetched for group ID", group_id, ":", len(projects)) + return projects + +def get_members(): + """Fetch all members of the organization.""" + print("🔨 Fetching organization members...") + members = [] + page = 1 + while True: + response = requests.get(f"{ORG_MEMBERS_URL}?page={page}&per_page=100", headers=HEADERS) + if response.status_code != 200: + print("❌ Failed to fetch members, status code:", response.status_code) + break + data = response.json() + if not data: + print("🔨 No more members to fetch.") + break + members.extend(data) + print(f"🔨 Fetched {len(data)} members from page {page}.") + page += 1 + print("🔨 Total members fetched:", len(members)) + return members + +def analyze_projects(projects, year): + """Analyze projects to gather various statistics.""" + print("🔨 Analyzing projects...") + total_projects = len(projects) + archived_projects = sum(1 for project in projects if project.get('archived', False)) + forked_projects = sum(1 for project in projects if project.get('fork', False)) + non_forked_projects = total_projects - forked_projects + + # Most stars and most forks + most_stars_project = max(projects, key=lambda p: p.get('star_count', 0), default=None) + most_forks_project = max(projects, key=lambda p: p.get('forks_count', 0), default=None) + + # Count projects created in a specific year + year_projects = sum(1 for project in projects if datetime.strptime(project['created_at'], '%Y-%m-%dT%H:%M:%S.%fZ').year == year) + + print("🔨 Analysis complete.") + return { + "total_projects": total_projects, + "archived_projects": archived_projects, + "forked_projects": forked_projects, + "non_forked_projects": non_forked_projects, + "most_stars_project": most_stars_project, + "most_forks_project": most_forks_project, + "year_projects": year_projects, + } + +# Services - Main +# --------------- + +def main(): + """Main function to execute the analysis.""" + check_prerequisites() + + # Parse command-line arguments + parser = argparse.ArgumentParser(description=f"Analyze GitLab organization groups and projects (Version: {VERSION}).") + parser.add_argument("--year", type=int, required=True, help="The year to analyze (e.g., 2024).") + args = parser.parse_args() + + start_time = time.time() + + subgroups = get_subgroups() + all_projects = [] + + for subgroup in subgroups: + projects = get_projects(subgroup['id']) + all_projects.extend(projects) + + total_members = get_members() + visible_members = len(total_members) + + # Analyze projects for the specified year + analysis = analyze_projects(all_projects, args.year) + + # Calculate elapsed time + elapsed_time = time.time() - start_time + + # Print the analysis results + print("\n-----------------------") + print("Analysis results below:\n") + print("👉 Total projects:", analysis["total_projects"], "\n") + print("👉 Archived projects:", analysis["archived_projects"], "\n") + print("👉 Forked projects:", analysis["forked_projects"], "\n") + print("👉 Non-forked projects:", analysis["non_forked_projects"], "\n") + print("👉 Most stars project:", analysis["most_stars_project"]["name"] if analysis["most_stars_project"] else "N/A", "\n") + print("👉 Most forks project:", analysis["most_forks_project"]["name"] if analysis["most_forks_project"] else "N/A", "\n") + print("👉 Projects created in", args.year, ":", analysis["year_projects"], "\n") + print("👉 Total members in organization (including outside collaborators):", len(total_members), "\n") + print("👉 Visible members:", visible_members, "\n") + print(f"\n⌛ Elapsed Time: {elapsed_time:.2f} seconds") # Print elapsed time + +# Services - Run +# -------------- + +if __name__ == "__main__": + main() diff --git a/toolbox/gitlab/requirements.txt b/toolbox/gitlab/requirements.txt new file mode 100644 index 0000000..3f8fa27 --- /dev/null +++ b/toolbox/gitlab/requirements.txt @@ -0,0 +1 @@ +python-dotenv==1.0.1 \ No newline at end of file From ee6ca79ea05058698642d2caf833a9c84de83ff3 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Mon, 27 Jan 2025 13:12:02 +0100 Subject: [PATCH 9/9] Prepare version v2.22.0 Signed-off-by: Pierre-Yves Lapersonne --- CHANGELOG.md | 6 ++++-- CITATION.cff | 4 ++-- README.md | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 650d735..d9d6baf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.21.0..dev) +## [Unreleased](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.22.0..dev) + +## [2.22.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.22.0..2.21.0) - 2025-01-27 ### Added - GitHub and GitLab year review ([#181](https://github.com/Orange-OpenSource/floss-toolbox/issues/181)) - Samples for common files to add in projects ([#184](https://github.com/Orange-OpenSource/floss-toolbox/issues/184)) -## [2.21.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.21.0..2.20.0) - 2024-09-164 +## [2.21.0](https://github.com/Orange-OpenSource/floss-toolbox/compare/2.21.0..2.20.0) - 2024-09-16 ### Added diff --git a/CITATION.cff b/CITATION.cff index 9ba60c2..6bb03ed 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -39,5 +39,5 @@ keywords: - audits - history license: Apache-2.0 -version: v2.21.0 -date-released: '2024-09-16' +version: v2.22.0 +date-released: '2025-01-27' diff --git a/README.md b/README.md index f60e78d..d70e70b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Opened issues](https://img.shields.io/github/issues-raw/Orange-OpenSource/floss-toolbox?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/issues) [![Apache 2.0 license](https://img.shields.io/github/license/Orange-OpenSource/floss-toolbox?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/blob/dev/LICENSE.txt) [![Versions](https://img.shields.io/github/v/release/Orange-OpenSource/floss-toolbox?label=Last%20version&style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/releases) -[![Still maintained](https://img.shields.io/maintenance/yes/2024?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/issues?q=is%3Aissue+is%3Aclosed) +[![Still maintained](https://img.shields.io/maintenance/yes/2025?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/issues?q=is%3Aissue+is%3Aclosed) [![Code size](https://img.shields.io/github/languages/code-size/Orange-OpenSource/floss-toolbox?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox) [![Shell](https://img.shields.io/badge/-Shell-89e051?style=for-the-badge)](https://github.com/Orange-OpenSource/floss-toolbox/search?l=shell)