{"id":26042563,"url":"https://github.com/0pb/smalldevops","last_synced_at":"2025-10-24T10:46:53.527Z","repository":{"id":57468560,"uuid":"300703360","full_name":"0pb/smalldevops","owner":"0pb","description":"Script and webpage for continuous integration (CI)","archived":false,"fork":false,"pushed_at":"2020-10-21T15:35:13.000Z","size":660,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-20T11:18:36.593Z","etag":null,"topics":["devops","graph","gui","javascript","local","python","small","testing","timeline","unittest","website"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0pb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-10-02T18:29:55.000Z","updated_at":"2023-11-10T00:41:06.000Z","dependencies_parsed_at":"2022-09-19T09:51:21.777Z","dependency_job_id":null,"html_url":"https://github.com/0pb/smalldevops","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/0pb/smalldevops","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0pb%2Fsmalldevops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0pb%2Fsmalldevops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0pb%2Fsmalldevops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0pb%2Fsmalldevops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0pb","download_url":"https://codeload.github.com/0pb/smalldevops/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0pb%2Fsmalldevops/sbom","scorecard":{"id":380,"data":{"date":"2025-08-11","repo":{"name":"github.com/0pb/smalldevops","commit":"c04f198f94241c278167618748854a172f506d8d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/12 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-14T12:22:26.201Z","repository_id":57468560,"created_at":"2025-08-14T12:22:26.201Z","updated_at":"2025-08-14T12:22:26.201Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280783848,"owners_count":26390277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-24T02:00:06.418Z","response_time":73,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devops","graph","gui","javascript","local","python","small","testing","timeline","unittest","website"],"created_at":"2025-03-07T16:34:24.794Z","updated_at":"2025-10-24T10:46:53.507Z","avatar_url":"https://github.com/0pb.png","language":"HTML","readme":"\n# SmallDevops\n\nSmall local/offline continuous developpement for python (currently for unittest only) with no dependency.\nIt consist in a python module and a webpage.\n\n## **Requirements**\n\n- Linux.\n- Unittest and pytest only.\n- Python3 (only the module require it, you can still launch your own script in Python2.7 if you wish).\n- No server (flask, django etc) is required, the website is a simple html file (work offline and on any local computer without additional setup).\n\n---\n\n## **How to download the project**\n\n`pip install SmallDevops` install the SmallDevops package. You can also directly use this project by cloning it and then using `python -m SmallDevops`.\n\n---\n\n## **How the webpage looks like** \n\nCommit with 3 failures in test : \n![website](example/smalldevops.png)\n\n- \"Passing test\" refers to amount of test failed / total amount of test.\n- \"Timing (program)\" come from the usr/bin/time command available on linux.\n\nRight column \n\n- top : commit - date - index of commit - total amount of test - button to make the graph full size\n- middle : Author and Commit message\n- List error : Grid of test case; It refer to name of the test case (when you write `class Test_main(unittest.TestCase):` for example)\n- List error (message) : Errors messages\n\n---\n\n## **How it work**\n\nThe python module will launch the script you pass as argument. Then it will recover informations like the time it took to run your script, the amount of test found by unittest, last git information, etc. and put it in a dictionnary.\n\nFinally It append this dictionnary containing the informations of the last run inside a javascript file. This javascript file is used by the webpage as a json object to display the graph. \n\nA javascript file is needed as you cannot load .json file in a local static webpage due to content policy.\n\n## **How to use it**\n\n`git init` in the folder you want (or you may want to use an already existing project).\n\nThen : \n\n### Basic : \n\n- `python -m SmallDevops create_website` create \"file.html\" in the current folder.\n- `python -m SmallDevops \"python3.6 script.py\"` execute \"python3.6 script.py\" and create a file \"output.js\" in the current folder.\n\nYou can now open file.html in your browser and voila. The page load the output.js file and display data.\n\nBecause the script only get the last commit data, the command `python -m SmallDevops \"python3.6 script.py\"` need to be run every time you commit.\n\nIt mean you either need to type the command each time you commit or you can use a post-commit hooks (like this [one](example/post-commit)) to automatically execute the script for every commit.\n\nYou can also ignore committing and simply run the script, however you will miss informations.\nYou cannot use this module without a git project (except if you do your own test class, shown later below). However you can git init, then git commit once and never bother committing again afterward.\n\n### Fully automatic : \n\n- `python -m SmallDevops create_website /path/where/you/want/the/website` create a \"file.html\" in the path given as argument.\n- create a hook post-commit or post-receive (if you put it on a git server) in your .git/hooks folder with this command : `exec python -m SmallDevops \"python script.py\" -dir=/path/to/script -output=/path/where/you/want/the/website`\n\nNow every you commit the script will run automatically and update the data accordingly.\n\n### If you want to execute your script with pytest :\n\nAdd `-template=pytest_timing_git` (ex : `python -m SmallDevops \"python3.6 script.py\" -template=pytest_timing_git`).\n\n### If you want to execute your script in python2.7 :\n\nAdd `-template=unittest_timing_git_python27` (ex : `python -m SmallDevops \"python2.7 script.py\" -template=unittest_timing_git_python27`).\n\nIf you want to use another version of python, either change your current python version (which the module detect with sys.version), or create your own test class and change the get_discover_command() function which run unittest.\n\n---\n\n## **How to add your own test class**\n\nThose test class are used by the SmallDevops as a way to get an output from a script, then process that output and put it in a directory.\n\nHere is an example for [unittest](SmallDevops/template_class/unittest_timing_git.py) and here is the [base template](SmallDevops/template_class/base_class_test.py).\n\nThe unittest_timing_git test class get the output, then process it trought different function. As an example if you wish to get the user time instead of the real time used by the module, you can copy unittest_timing_git.py, then change the get_execution_time() function like so :\n\n```\n\ndef get_execution_time(self, command : list, true_if_print_output : bool) -\u003e float:\n    str_command = shlex.split('\\\\time -f \"%E|%U|%S\" ') + command\n    output_from_execution = execute_command_in_cmd(str_command)[0]\n    if true_if_print_output:\n        print(output_from_execution)\n    timing = str(output_from_execution).split(\"\\n\")\n        return float(timing[-2].split(\"|\")[1])\n                                           ^ Where the change is done\n```\n\nOnce you do that and save the modified file as my_own_test_class.py, you can either re-build the package, or you can use the `-template` and `-template_path option` to specify which test class you want to use.\n\nIn the second case, use this command from now on : `python -m SmallDevops \"python2.7 script.py\" -template=my_own_test_class -template=/path/where/you/put/your/test/class`\n\n--- \n\n## **List of arguments (from devop.py)** : \n\n```\npython -m SmallDevops \"[command to execute]\"|create_website [list arg]\n            -dir\n            -show\n            -output\n            -nooutput\n            -template\n            -path_template\n\nlist argument possible =\n    \"-dir\" : cd inside that dir for executing the script given as command\n             is required if you execute a script from another folder\n        ex: -dir=/relative/path/script\n            -dir=/absolute/path/to/different/script\n\n    \"-show\" : show the output from the command executed, ex a script that print\n            \"hello\" to the console will then print \"hello\n                                                    json created\n                                                    devop script done\"\n        ex: -show\n\n    \"-output\" : create the output file in the corresponding folder\n                if the output is a path (/absolute/path/), then a output.js will be created at that\n                location\n                if the output is a path with a file name (/absolute/path/filename.js) which mean an\n                extension, then the file \"filename.js\" will be created at that path\n                if the output is simply a file name (filename.js) the file \"filename.js\" will be\n                created in the folder where the SmallDevops script has been executed, NOT in the folder\n                in the \"-dir\" option\n        ex: -output=/absolute/path/folder/\n            -output=/relative/path/data.js\n            -output=/relative/path/data.random_ext\n            -output=data.output\n\n    \"-nooutput\" : doesn't create an output file\n        ex: -nooutput\n\n    \"-template\" : use the corresponding template, require -path_template\n        ex: -template=pytest_timing_git\n\n    \"-path_template\" : fetch the corresponding template, require -template\n        ex: -path_external_template=/absolute/path/to/template\n```\n\n---\n\n## **Features**\n\n- Lots of options for customisations.\n- Interactive graph (click on a point and get its informations).\n- Self-sufficient, doesn't require node.js or any specific python library.\n- Doesn't modify your project in any way, doesn't require any special file or line to be added in your project.\n- You can put the graph as fullscreen if you click on the arroys icon in the right column.\n- Can be easily modified to suit a server and an ajax request.\n- You can easily add your own \"test class\" (let's say you use something else than unittest or pytest, you can easily create a test class for your specific test class).\n- Quick (1000+ entry data doesn't slow down the site), js file is around 1Mb~ for 550 entry (=550 commits).\n\n\n---\n\n## Issues\n\n- Work on latest Firefox, didn't test on chrome, opera or IE.\n- Work on ubuntu, didn't test on other distro or windows. The biggest problem that could arise would be the time function and the stdout not being recognized.\n- Only support unittest by default for now.\n- **Doesn't get every data from git** : If you decide to use it in a project with lot of commit already, it *won't* get the past commit data and information. It would require the script to run throught every commit in the right order, run the test and record those data. I might add that feature in the futur but for now it is not possible.\n\n---\n\n## **Specification \u0026 why**\n\nThis project use : [Bootstrap](https://getbootstrap.com/), [chartjs](https://www.chartjs.org/), [jquery](https://jquery.com/), [luxon](https://moment.github.io/luxon/) as javascript library.\n\nI wish to work in devops so this project was interesting to make. It also highlighted many problems in devops (how to make sure the programs is running correctly, which data to recover, ..).\n\nIf possible I will improve this project for including other test suite. I could also improve the data recovered.\n\n---\n\n## **How to build the package**\n\nUse `python setup.py sdist bdist_wheel` (change to python3.7 or whatever you want).\nDon't forget to check with twine afterward.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0pb%2Fsmalldevops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0pb%2Fsmalldevops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0pb%2Fsmalldevops/lists"}