From fea76908bfe66e6ffd10e516d36790347d5a0f77 Mon Sep 17 00:00:00 2001 From: Wolf Date: Wed, 22 May 2024 19:04:38 +0100 Subject: [PATCH] The initial commit --- .github/CODEOWNERS | 5 + .github/CODE_OF_CONDUCT.md | 75 +++ .github/CONTRIBUTING.md | 14 + .github/FUNDING.yml | 4 + .github/ISSUE_TEMPLATE/ask_question.yml | 22 + .github/ISSUE_TEMPLATE/bug_report.yml | 58 +++ .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.yml | 43 ++ .github/PULL_REQUEST_TEMPLATE.md | 36 ++ .github/SECURITY.md | 40 ++ .github/dependabot.yml | 18 + .github/scripts/check-jobs.sh | 38 ++ .github/workflows/cicd.yml | 65 +++ .github/workflows/citation-validation.yml | 84 +++ .../workflows/delete-old-workflow-runs.yml | 75 +++ .github/workflows/dependabot.yml | 53 ++ .github/workflows/document-validation.yml | 102 ++++ .github/workflows/generate-release.yml | 62 +++ .github/workflows/generate-test-release.yml | 60 +++ .github/workflows/greetings.yml | 27 + .../purge-deprecated-workflow-runs.yml | 47 ++ .github/workflows/repository-validation.yml | 100 ++++ .github/workflows/security-hardening.yml | 33 ++ .github/workflows/stale.yml | 57 ++ .gitignore | 11 + .yamllint | 26 + CITATION.cff | 15 + LICENSE.md | 25 + README.md | 150 ++++++ pipeline.sh | 492 ++++++++++++++++++ 30 files changed, 1845 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/ask_question.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SECURITY.md create mode 100644 .github/dependabot.yml create mode 100755 .github/scripts/check-jobs.sh create mode 100644 .github/workflows/cicd.yml create mode 100644 .github/workflows/citation-validation.yml create mode 100644 .github/workflows/delete-old-workflow-runs.yml create mode 100644 .github/workflows/dependabot.yml create mode 100644 .github/workflows/document-validation.yml create mode 100644 .github/workflows/generate-release.yml create mode 100644 .github/workflows/generate-test-release.yml create mode 100644 .github/workflows/greetings.yml create mode 100644 .github/workflows/purge-deprecated-workflow-runs.yml create mode 100644 .github/workflows/repository-validation.yml create mode 100644 .github/workflows/security-hardening.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 CITATION.cff create mode 100644 LICENSE.md create mode 100644 README.md create mode 100755 pipeline.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..50be806 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# +# These owners will be the default owners for everything in the repo. +# +* @TGWolf + diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5ffa3a8 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, 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. + +## Our Standards + +Examples of behaviour that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behaviour by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behaviour and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behaviour. + +Project maintainers 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, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behaviour may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the +[Contributor Covenant](https://www.contributor-covenant.org), version 1.4, +available at + +For answers to common questions about this code of conduct, see + diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..145a864 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,14 @@ +# Contributing + +Please refer to the +[contributing](https://github.com/WolfSoftware/contributing) +documentation. + +## Important + +ALL commits must be signed to ensure the identity of the developer, any pull +requests that are made with unsigned commits will be rejected as a matter of +course. + +> This project has a [code of conduct](CODE_OF_CONDUCT.md). By interacting +with this repository, organization, or community you agree to abide by its terms. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b7a1e2f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# Funding +# https://help.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository + +github: [WolfSoftware,TGWolf] diff --git a/.github/ISSUE_TEMPLATE/ask_question.yml b/.github/ISSUE_TEMPLATE/ask_question.yml new file mode 100644 index 0000000..b13f409 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ask_question.yml @@ -0,0 +1,22 @@ +name: Ask a question +description: If you don't have a specific issue or bug to report you can still ask us questions and we will do our best to answer them +title: "[Question]: " +labels: ["type: question", "state: triage"] +assignees: + - tgwolf +body: + - type: textarea + id: question + attributes: + label: What is your question? + description: Please give us time to review your question and formulate an answer. + validations: + required: true + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/CICDToolbox/validate-citations-file/blob/master/.github/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..c08573a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,58 @@ +name: Report a bug +description: Found a bug? Let us knonw what the issue is and we will attempt to fix it +title: "[Bug]: " +labels: ["type: bug", "state: triage"] +assignees: + - tgwolf +body: + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: How do we reproduct the bug? + description: What are the steps we need to take to reproduce the behavior? + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: false + - type: textarea + id: screenshoots + attributes: + label: Screeenshots + description: Upload any screenshots that might help demonstrate the bug. + validations: + required: false + - type: textarea + id: additional-information + attributes: + label: Additional information + description: Please provide any additional information that you think will help us to resolve this bug. + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/CICDToolbox/validate-citations-file/blob/master/.github/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..cabd7e8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Support us + url: https://ko-fi.com/wolfsoftware + about: Show your support + - name: Visit our website + url: https://wolfsoftware.com/ + about: Visit the Wolf Software website and see what else we do and what services we offer diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..364f40c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,43 @@ +name: Request a new feature +description: Got an idea for a new feature? Let us know what you want and we will see if we can add it +title: "[Feature Request]: " +labels: ["type: feature", "state: triage"] +assignees: + - tgwolf +body: + - type: textarea + id: releated-to + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem is. E.g. I'm always frustrated when ... + validations: + required: true + - type: textarea + id: suggested-solution + attributes: + label: Suggested Solution + description: A clear and concise description of what you want to see implemented. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + - type: textarea + id: additional-information + attributes: + label: Additional information + description: Please provide any additional information that you think will help us to resolve this bug. + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/CICDToolbox/validate-citations-file/blob/master/.github/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0e99242 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,36 @@ +# Thank You + +Thanks for submitting a pull request! Please provide enough information so that +others can review your pull request: + +## Summary + + + +This PR fixes/implements the following **bugs/features** + +* [ ] Bug 1 +* [ ] Feature 1 +* [ ] Breaking changes + + + +Explain the **motivation** for making this change. What existing problem does +the pull request solve? + + + +## Test plan (required) + +Demonstrate the code is solid. Example: The exact commands you ran and their +output, screenshots help greatly. + + + +## Closing issues + + +Fixes # diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..b46843b --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policies and Procedures + +This document outlines security procedures and general policies for this project. + +* [Reporting a Bug](#reporting-a-bug) +* [Disclosure Policy](#disclosure-policy) +* [Comments on this Policy](#comments-on-this-policy) + +## Reporting a Bug + +We take **ALL** security related bugs and issues very seriously. + +If you think you have identified a security related issue, please +[report it immediately](mailto:disclose@wolfsoftware.com) and include +the word "SECURITY" in the subject line. If you are not sure, don’t worry. +Better safe than sorry – just send an email. + +* Please provide as much information as you can. +* Please do not open issues related to any security concerns publicly. +* Please do not include anyone else on the disclosure email. + +Report security bugs in third-party modules to the person or team maintaining +the module. + +## Disclosure Policy + +When a security report is received, we will carry out the following steps: + +* Confirm the problem and determine the affected versions. +* Audit code to find any potential similar problems. +* Prepare fixes for all releases still under maintenance. These fixes will be + released as fast as possible. + +We will endeavour to keep you informed of the progress towards a fix and full +announcement, and may ask for additional information or guidance. + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a +pull request. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c9f5bfa --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "04:00" + open-pull-requests-limit: 10 + commit-message: + prefix: "chore:" + labels: + - "dependabot: ecosystem : github actions" + - "dependabot: dependencies" + assignees: + - "TGWolf" + diff --git a/.github/scripts/check-jobs.sh b/.github/scripts/check-jobs.sh new file mode 100755 index 0000000..e35587c --- /dev/null +++ b/.github/scripts/check-jobs.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# This script receives a JSON string containing job results and checks for any failures. + +# Check if jq is available +if ! command -v jq &> /dev/null; then + echo "jq could not be found, please install jq to run this script." + exit 1 +fi + +# Read the JSON string from the first script argument +job_results_json=$1 + +# Check if the job results JSON is not empty +if [[ -z "$job_results_json" ]]; then + echo "No job results JSON provided." + exit 1 +fi + +# Set default state +failed_jobs=false + +# Use jq to parse the JSON and check each job's result +while IFS= read -r line; do + job_name=$(echo "$line" | awk '{print $1}') + result=$(echo "$line" | awk '{print $3}') + + if [ "$result" != "success" ]; then + echo "$job_name failed." + failed_jobs=true + else + echo "$job_name succeed." + fi +done <<< "$( echo "$job_results_json" | jq -r 'to_entries[] | "\(.key) result: \(.value.result)"' )" + +if [ "$failed_jobs" = true ] ; then + exit 1 +fi diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000..cb38ffd --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,65 @@ +name: CI/CD Pipeline + +on: + push: + branches-ignore: + - 'dependabot/**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + + pull_request: + branches: + - '**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Perform ShellCheck Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/shellcheck/master/pipeline.sh) + + validate-citations-file: + name: Validate Citations File + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Setup Python 3.12 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + with: + python-version: "3.12" + + - name: Run Validate Citations File + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/validate-citations-file/master/pipeline.sh) + + cicd-pipeline: + if: always() + name: CI/CD Pipeline + needs: + - shellcheck + - validate-citations-file + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/citation-validation.yml b/.github/workflows/citation-validation.yml new file mode 100644 index 0000000..f61005b --- /dev/null +++ b/.github/workflows/citation-validation.yml @@ -0,0 +1,84 @@ +name: Citation Validation + +on: + push: + branches-ignore: + - 'dependabot/**' + paths: + - 'CITATION.cff' + pull_request: + branches: + - '**' + paths: + - 'CITATION.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + get-ruby-version: + name: Get Latest Ruby Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Version + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # v0.1.3 + id: get-language-versions + with: + language: "ruby" + highest-only: true + remove-patch-version: true + + awesomebot: + name: Awesomebot + needs: get-ruby-version + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Setup Ruby ${{ needs.get-ruby-version.outputs.version }} + uses: ruby/setup-ruby@943103cae7d3f1bb1e4951d5fcc7928b40e4b742 # v1.177.1 + with: + ruby-version: ${{ needs.get-ruby-version.outputs.version }} + + - name: Perform Awesomebot Analysis + env: + FLAGS: "default" + WHITELIST: "https://img.shields.io" + INCLUDE_FILES: "CITATION.cff" + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/awesomebot/master/pipeline.sh) + + validate-citation-file: + name: Validate CITATION.cff + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Validate CITATION.cff + uses: citation-file-format/cffconvert-github-action@4cf11baa70a673bfdf9dad0acc7ee33b3f4b6084 # v2.0.0 + with: + args: "--validate" + + citation-validation-pipeline: + if: always() + name: Citation Validation Pipeline + needs: + - awesomebot + - validate-citation-file + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/delete-old-workflow-runs.yml b/.github/workflows/delete-old-workflow-runs.yml new file mode 100644 index 0000000..a094c8b --- /dev/null +++ b/.github/workflows/delete-old-workflow-runs.yml @@ -0,0 +1,75 @@ +name: Delete Old Workflow Runs + +on: + workflow_dispatch: + inputs: + days: + description: 'Number of days to retain workflow runs.' + required: true + default: '30' + minimum-runs: + description: 'The minimum number of runs to keep for each workflow.' + required: true + default: '6' + + schedule: + - cron: '20 5 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + DAYS: 30 + MINIMUM_RUNS: 6 + +permissions: read-all + +jobs: + set-runtime-values: + name: Set Runtime Values + runs-on: ubuntu-latest + outputs: + days: ${{ steps.set-output-defaults.outputs.days }} + minimum-runs: ${{ steps.set-output-defaults.outputs.minimum-runs }} + + steps: + - name: Set Runtime Values + id: set-output-defaults + run: | + echo "days=${{ github.event.inputs.days || env.DAYS }}" >> "${GITHUB_OUTPUT}" + echo "minimum-runs=${{ github.event.inputs.minimum-runs || env.MINIMUM_RUNS }}" >> "${GITHUB_OUTPUT}" + + delete-old-workflows: + name: Delete Old Workflow Runs + runs-on: ubuntu-latest + permissions: + actions: write + needs: + - set-runtime-values + + steps: + - name: Delete Old Workflow Runs + uses: Mattraks/delete-workflow-runs@39f0bbed25d76b34de5594dceab824811479e5de # v2.0.6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + retain_days: ${{ needs.set-runtime-values.outputs.days }} + keep_minimum_runs: ${{ needs.set-runtime-values.outputs.minimum-runs }} + + slack-workflow-status: + if: always() + name: Slack Post Workflow Notification + needs: + - delete-old-workflows + runs-on: ubuntu-latest + + steps: + - name: Slack Workflow Notifications + if: ${{ github.event_name == 'schedule' && needs.delete-old-workflows.result != 'success'}} + uses: Gamesight/slack-workflow-status@68bf00d0dbdbcb206c278399aa1ef6c14f74347a # v1.3.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + include_jobs: on-failure + include_commit_message: true diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..247ba9d --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,53 @@ +name: Dependabot Pull Request Approve & Merge + +on: pull_request + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + dependabot: + name: Dependabot + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Fetch Metadata + id: dependabot-metadata + uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Approve PR + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor' }} + run: | + gh pr review --approve "${PR_URL}" -b "I'm **approving** this pull request because it includes a patch or minor update" + gh pr edit "${PR_URL}" --add-label "dependabot: auto approve" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Auto-Merge Non-Major Updates + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor' }} + run: | + gh pr comment "${PR_URL}" --body "I'm automatically merging this PR because it includes a patch or minor update" + gh pr merge --auto --squash --delete-branch "${PR_URL}" + gh pr edit "${PR_URL}" --add-label "dependabot: auto merge" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment & Label Major Updates + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' }} + run: | + gh pr comment "${PR_URL}" --body "I'm **NOT** automatically merging this PR because it includes a major update of a dependency" + gh pr edit "${PR_URL}" --add-label "dependabot: manual merge" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/document-validation.yml b/.github/workflows/document-validation.yml new file mode 100644 index 0000000..797590e --- /dev/null +++ b/.github/workflows/document-validation.yml @@ -0,0 +1,102 @@ +name: Documentation Validation + +on: + push: + branches-ignore: + - 'dependabot/**' + paths: + - '**/*.md' + pull_request: + branches: + - '**' + paths: + - '**/*.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + get-node-version: + name: Get Latest Node Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Version + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # v0.1.3 + id: get-language-versions + with: + language: "node" + highest-only: true + remove-patch-version: true + + get-ruby-version: + name: Get Latest Ruby Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Version + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # v0.1.3 + id: get-language-versions + with: + language: "ruby" + highest-only: true + remove-patch-version: true + + awesomebot: + name: Awesomebot + needs: get-ruby-version + runs-on: ubuntu-latest + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Setup Ruby ${{ needs.get-ruby-version.outputs.version }} + uses: ruby/setup-ruby@943103cae7d3f1bb1e4951d5fcc7928b40e4b742 # v1.177.1 + with: + ruby-version: ${{ needs.get-ruby-version.outputs.version }} + + - name: Perform Awesomebot Analysis + env: + FLAGS: "default" + WHITELIST: "https://img.shields.io" + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/awesomebot/master/pipeline.sh) + + markdown-lint: + name: Markdown Lint + needs: get-node-version + runs-on: ubuntu-latest + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Setup Node ${{ needs.get-node-version.outputs.version }} + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: ${{ needs.get-node-version.outputs.version }} + + - name: Perform Markdown Lint Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/markdown-lint/master/pipeline.sh) + env: + EXCLUDE_FILES: "README.md" + + repository-validation-pipeline: + if: always() + name: Documentation Validation Pipeline + needs: + - awesomebot + - markdown-lint + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/generate-release.yml b/.github/workflows/generate-release.yml new file mode 100644 index 0000000..37682a4 --- /dev/null +++ b/.github/workflows/generate-release.yml @@ -0,0 +1,62 @@ +name: Generate a Release + +on: + push: + tags: + - 'v[0-9].[0-9]+.[0-9]+' + - '!v[0-9].[0-9]+.[0-9]+rc[0-9]+' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + set-release-version: + name: Set Release Version + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.set-release-version.outputs.release-version }} + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + + - name: Set the Release Version + id: set-release-version + run: | + echo "release-version=${GITHUB_REF#refs/*/}" >> "${GITHUB_OUTPUT}" + + create-release: + name: Create a Release + permissions: + contents: write + runs-on: ubuntu-latest + needs: + - set-release-version + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + + - name: Generate Changelog + uses: Bullrich/generate-release-changelog@6b60f004b4bf12ff271603dc32dbd261965ad2f2 # v2.0.2 + id: Changelog + env: + REPO: ${{ github.repository }} + + - name: Create a Release + id: create_release + uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ github.ref }} + name: ${{ needs.set-release-version.outputs.release-version }} + body: ${{ steps.Changelog.outputs.changelog }} + draft: false + prerelease: false diff --git a/.github/workflows/generate-test-release.yml b/.github/workflows/generate-test-release.yml new file mode 100644 index 0000000..b4b3fe7 --- /dev/null +++ b/.github/workflows/generate-test-release.yml @@ -0,0 +1,60 @@ +name: Generate a TEST Release + +on: + push: + tags: + - 'v[0-9].[0-9]+.[0-9]+rc[0-9]+' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + set-release-version: + name: Set Release Version + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.set-release-version.outputs.release-version }} + + steps: + - name: Checkout the repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + + - name: Set the release version + id: set-release-version + run: echo "release-version=${GITHUB_REF#refs/*/}" >> "${GITHUB_OUTPUT}" + + create-release: + name: Create Release + permissions: + contents: write + runs-on: ubuntu-latest + needs: + - set-release-version + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + + - name: Generate Changelog + uses: Bullrich/generate-release-changelog@6b60f004b4bf12ff271603dc32dbd261965ad2f2 # v2.0.2 + id: Changelog + env: + REPO: ${{ github.repository }} + + - name: Create a Release + id: create_release + uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ github.ref }} + name: ${{ needs.set-release-version.outputs.release-version }} + body: ${{ steps.Changelog.outputs.changelog }} + draft: false + prerelease: true diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000..ddd7cd8 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,27 @@ +name: Greetings + +on: + pull_request: + issues: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + greetings: + name: Handle Greetings + permissions: + issues: write + pull-requests: write + runs-on: ubuntu-latest + + steps: + - name: Handle Greetings + uses: actions/first-interaction@34f15e814fe48ac9312ccf29db4e74fa767cbab7 # v1.3.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: "Thank you for raising your first issue - all contributions to this project are welcome!" + pr-message: "Thank you for raising your first pull request - all contributions to this project are welcome!" diff --git a/.github/workflows/purge-deprecated-workflow-runs.yml b/.github/workflows/purge-deprecated-workflow-runs.yml new file mode 100644 index 0000000..ec24f33 --- /dev/null +++ b/.github/workflows/purge-deprecated-workflow-runs.yml @@ -0,0 +1,47 @@ +name: Purge Deprecated Workflow Runs + +on: + workflow_dispatch: + + schedule: + - cron: '26 5 * * 1' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + purge-obsolete-workflows: + name: Purge Deprecated Workflow Runs + permissions: + actions: write + runs-on: ubuntu-latest + + steps: + - name: Purge Deprecated Workflow Runs + uses: otto-de/purge-deprecated-workflow-runs@31a4e821d43e9a354cbd65845922c76e4b4b3633 # v 2.0.4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + remove-obsolete: true + remove-cancelled: true + remove-failed: true + remove-skipped: true + + slack-workflow-status: + if: always() + name: Slack Post Workflow Notification + needs: + - purge-obsolete-workflows + runs-on: ubuntu-latest + + steps: + - name: Slack Workflow Notifications + if: ${{ github.event_name == 'schedule' && needs.purge-obsolete-workflows.result != 'success'}} + uses: Gamesight/slack-workflow-status@68bf00d0dbdbcb206c278399aa1ef6c14f74347a # v1.3.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + include_jobs: on-failure + include_commit_message: true diff --git a/.github/workflows/repository-validation.yml b/.github/workflows/repository-validation.yml new file mode 100644 index 0000000..8f0c10e --- /dev/null +++ b/.github/workflows/repository-validation.yml @@ -0,0 +1,100 @@ +name: Repository Validation + +on: + push: + branches-ignore: + - 'dependabot/**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + pull_request: + branches: + - '**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + get-go-version: + name: Get Latest Go Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Versions + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # V0.1.3 + id: get-language-versions + with: + language: "go" + highest-only: true + remove-patch-version: true + + get-python-version: + name: Get Latest Python Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Versions + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # V0.1.3 + id: get-language-versions + with: + language: "python" + highest-only: true + remove-patch-version: true + + action-lint: + name: Action Lint + needs: get-go-version + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # V4.1.6 + + - name: Setup Go ${{ needs.get-go-version.outputs.version }} + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # V5.0.1 + with: + go-version: ${{ needs.get-go-version.outputs.version }} + + - name: Perform Action Lint Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/action-lint/master/pipeline.sh) + + yaml-lint: + name: YAML Lint + needs: get-python-version + runs-on: ubuntu-latest + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # V4.1.6 + + - name: Set up Python ${{ needs.get-python-version.outputs.version }} + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # V5.1.0 + with: + python-version: ${{ needs.get-python-version.outputs.version }} + + - name: Perform YAML Lint Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/yaml-lint/master/pipeline.sh) + + repository-validation-pipeline: + if: always() + name: Repository Validation Pipeline + needs: + - action-lint + - yaml-lint + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # V4.1.6 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/security-hardening.yml b/.github/workflows/security-hardening.yml new file mode 100644 index 0000000..4064ce5 --- /dev/null +++ b/.github/workflows/security-hardening.yml @@ -0,0 +1,33 @@ +name: Security Hardening + +on: + push: + branches-ignore: + - 'dependabot/**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + pull_request: + branches: + - '**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + security-hardening: + name: Harden Security + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + + - name: Ensure SHA Pinned Actions + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@40e45e738b3cad2729f599d8afc6ed02184e1dbd # v3.0.5 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..597a30b --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,57 @@ +name: Stale Issue & PR Handler + +on: + schedule: + - cron: '28 5 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + stale: + name: Handle Stale Issues & PRs + permissions: + contents: write + issues: write + pull-requests: write + runs-on: ubuntu-latest + + steps: + - name: Handle Stale Issues & PRs + uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 + id: stale-issues + with: + stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' + close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' + days-before-issue-stale: 30 + days-before-issue-close: 5 + stale-issue-label: 'state: stale' + close-issue-label: 'resolution: closed' + exempt-issue-labels: 'state: blocked,state: keep' + stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.' + close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.' + days-before-pr-stale: 45 + days-before-pr-close: 10 + stale-pr-label: 'state: stale' + close-pr-label: 'resolution: closed' + exempt-pr-labels: 'state: blocked,state: keep' + + slack-workflow-status: + if: always() + name: Slack Post Workflow Notification + needs: + - stale + runs-on: ubuntu-latest + + steps: + - name: Slack Workflow Notifications + if: ${{ github.event_name == 'schedule' && needs.stale.result != 'success'}} + uses: Gamesight/slack-workflow-status@68bf00d0dbdbcb206c278399aa1ef6c14f74347a # v1.3.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + include_jobs: on-failure + include_commit_message: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20f4490 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +### Hard Coded Minimums ### +# +# Because I use a Mac +# +**/.DS-Store + +# +# Ignore Awesomebot output +# +**/ab-results* + diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..804e6c1 --- /dev/null +++ b/.yamllint @@ -0,0 +1,26 @@ +--- + +extends: default + +rules: + braces: + level: warning + max-spaces-inside: 1 + brackets: + level: warning + max-spaces-inside: 1 + colons: + level: warning + commas: + level: warning + comments-indentation: disable + document-start: disable + empty-lines: + level: warning + hyphens: + level: warning + indentation: + level: warning + indent-sequences: consistent + line-length: disable + truthy: disable diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..1e84848 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,15 @@ +cff-version: 1.2.0 +message: If you use this software, please cite it using these metadata. +title: Validate Citation File +abstract: Ensure that your CITATION.cff is valid. +type: software +version: 0.1.0 +date-released: 2024-05-22 +repository-code: https://github.com/CICDToolbox/validate-citation +keywords: + - "Wolf Software" + - "Software" +license: MIT +authors: + - family-names: "Wolf" + orcid: "https://orcid.org/0009-0007-0983-2072" diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c14811f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2009-2024` `Wolf Software` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c7b38d7 --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ + +

+ + CICDToolbox logo + +
+ + Github Build Status + + + License + + + Created + +
+ + Release + + + Released + + + Commits since release + +
+ + + + + + + + + + + + +

+ +## Overview + +A tool to validate your CITATIONS.cff file using [cffconvert](https://pypi.org/project/cffconvert/). + +This tool has been tested against the following: + +1. GitHub Actions +2. Travis CI +3. CircleCI +4. BitBucket pipelines +5. Local command line + +However due to the way that they are built they should work on most CICD platforms where you can run arbitrary scripts. + +We provide a [script](https://github.com/CICDToolbox/get-all-tools) which pulls the latest copy of all the CICD tools and +places them in a local bin directory to allow them to be run any time locally for added validation. + +## Basic Usage + +```yml +on: [push, pull_request] + +jobs: + build: + name: Validate Citations File + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@v4 + + - name: Setup Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run Validate Citations File + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/validate-citations-file/master/pipeline.sh) +``` + +### Configuration Options + +The following environment variables can be set in order to customise the script. + +| Name | Default Value | Purpose | +| :------------ | :-----------: | :-------------------------------------------------------------------------------------------------------------- | +| INCLUDE_FILES | Unset | A comma separated list of files to include for being scanned. You can also use `regex` to do pattern matching. | +| EXCLUDE_FILES | Unset | A comma separated list of files to exclude from being scanned. You can also use `regex` to do pattern matching. | +| NO_COLOR | False | Turn off the color in the output. | +| REPORT_ONLY | False | Generate the report but do not fail the build even if an error occurred. | +| SHOW_ERRORS | True | Show the actual errors instead of just which files had errors. | +| SHOW_SKIPPED | False | Show which files are being skipped. | + +> If you set INCLUDE_FILES - it will skip ALL files that do not match, including anything in EXCLUDE_FILES. + +You can use any combination of the above settings. + +```yml +on: [push, pull_request] + +jobs: + build: + name: Validate Citations File + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@v4 + + - name: Setup Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run Validate Citations File + env: + REPORT_ONLY: true + SHOW_ERRORS: true + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/validate-citations-file/master/pipeline.sh) +``` + +## Example Output + +This is an example of the output report generated by this tool, this is the actual output from the tool running against itself. + +``` +--------------------------------------------------------------------- Stage 1: Parameters -- + No parameters given +---------------------------------------------------------- Stage 2: Install Prerequisites -- + [ OK ] python -m pip install --quiet --upgrade pip + [ OK ] cffconvert --validate is already installed +-------------------------------------------------------- Stage 3: Run cffconvert (v2.0.0) -- + [ OK ] CITATION.cff +------------------------------------------------------------------------- Stage 4: Report -- + Total: 1, OK: 1, Failed: 0, Skipped: 0 +----------------------------------------------------------------------- Stage 5: Complete -- +``` + +## File Identification + +Target files are identified using the following code: + +```shell +[[ ${filename} =~ CITATIONS.cff$ ]] +``` + +There is not magic type for puppet files so file -b is of not use for identifying the files. + +
+

diff --git a/pipeline.sh b/pipeline.sh new file mode 100755 index 0000000..aacd99a --- /dev/null +++ b/pipeline.sh @@ -0,0 +1,492 @@ +#!/usr/bin/env bash + +# -------------------------------------------------------------------------------- # +# Description # +# -------------------------------------------------------------------------------- # +# This script will locate and process all relevant files within the given git # +# repository. Errors will be stored and a final exit status used to show if a # +# failure occurred during the processing. # +# -------------------------------------------------------------------------------- # + +# -------------------------------------------------------------------------------- # +# Configure the shell. # +# -------------------------------------------------------------------------------- # + +set -Eeuo pipefail + +# -------------------------------------------------------------------------------- # +# Global Variables # +# -------------------------------------------------------------------------------- # + +# Are you using a perl based tool? +PERL_BASED_TOOL=false + +# Are you using a python based tool? +PYTHON_BASED_TOOL=true +INSTALL_REQUIREMENTS=false + +# Are you using a ruby gem based tool? +RUBY_GEM_BASED_TOOL=false +RUBY_GEM_NAME='' + +# Are you using a docker based tool? +DOCKER_BASED_TOOL=false +DOCKER_CONTAINER='' +DOCKER_CONTAINER_SHORT='' + +# How to install the require tool - eg gem install or pip install +INSTALL_COMMAND=('pip' 'install' '--quiet' 'cffconvert') + +# The specific command to run when running a test +TEST_COMMAND=('cffconvert' '--validate') + +# Version Banner - What to show on the version banned +BANNER_NAME="cffconvert" + +# File type to match (comes from file -b) [Regex based] +FILE_TYPE_SEARCH_PATTERN='No Magic Pattern' + +# File name to match [Regex based] +FILE_NAME_SEARCH_PATTERN='^CITATION.cff$' + +# Set where to look for files. +SCAN_ROOT='.' + +# -------------------------------------------------------------------------------- # +# Script Specific Global Variables # +# -------------------------------------------------------------------------------- # +# +# Add anything specific to this tool here +# + +# -------------------------------------------------------------------------------- # +# Tool Specific Functions # +# -------------------------------------------------------------------------------- # + +function handle_non_standard_parameters() +{ + local parameters=false + local retval=0 + + + if [[ "${parameters}" != true ]]; then + retval=1 + fi + + return "${retval}" +} + +function check_file() +{ + local filename=$1 + local errors + + file_count=$((file_count + 1)) + # shellcheck disable=SC2310 + if ! errors=$(run_command "${TEST_COMMAND[@]}" < "${filename}"); then + fail "${filename}" "${errors}" + fail_count=$((fail_count + 1)) + else + success "${filename}" + ok_count=$((ok_count + 1)) + fi +} + +# -------------------------------------------------------------------------------- # +# Stop Here # +# # +# Everything below here is standard and designed to work with all of the tools # +# that have been built and released as part of the CICDToolbox. # +# -------------------------------------------------------------------------------- # + +EXIT_VALUE=0 +CURRENT_STAGE=0 + +# -------------------------------------------------------------------------------- # +# Utility Functions # +# -------------------------------------------------------------------------------- # + +function run_command() +{ + local command=("$@") + + if ! output=$("${command[@]}" 2>&1); then + echo "${output}" + return 1 + fi + echo "${output}" + return 0 +} + +function stage() +{ + local message=${1:-} + + CURRENT_STAGE=$((CURRENT_STAGE + 1)) + align_right "${bold_text}${cyan_text}Stage ${CURRENT_STAGE}: ${message}${reset}" +} + +function success() +{ + local message=${1:-} + + echo " [ ${bold_text}${green_text}OK${reset} ] ${message}" +} + +function fail() +{ + local message=${1:-} + local errors=${2:-} + local override=${3:-false} + + echo " [ ${bold_text}${red_text}FAIL${reset} ] ${message}" + + if [[ "${SHOW_ERRORS}" == true || "${override}" == true ]]; then + if [[ -n "${errors}" ]]; then + echo + echo "${errors}" | while IFS= read -r err; do + echo " ${err}" + done + echo + fi + fi + + EXIT_VALUE=1 +} + +function skip() +{ + local message=${1:-} + + if [[ "${SHOW_SKIPPED}" == true ]]; then + skip_count=$((skip_count + 1)) + echo " [ ${bold_text}${yellow_text}Skip${reset} ] ${message}" + fi +} + +function is_excluded() +{ + local needle=$1 + + for pattern in "${exclude_list[@]}"; do + if [[ "${needle}" =~ ${pattern} ]]; then + return 0 + fi + done + return 1 +} + +function is_included() +{ + local needle=$1 + + for pattern in "${include_list[@]}"; do + if [[ "${needle}" =~ ${pattern} ]]; then + return 0 + fi + done + return 1 +} + +function align_right() +{ + local message=${1:-} + local offset=${2:-2} + local width=${screen_width} + + local clean + clean=$(strip_colours "${message}") + local textsize=${#clean} + + local left_line='-' left_width=$((width - (textsize + offset + 2))) + local right_line='-' right_width=${offset} + + while ((${#left_line} < left_width)); do left_line+="${left_line}"; done + while ((${#right_line} < right_width)); do right_line+="${right_line}"; done + + printf '%s %s %s\n' "${left_line:0:left_width}" "${message}" "${right_line:0:right_width}" +} + +function strip_colours() +{ + local orig=${1:-} + + if ! shopt -q extglob; then + shopt -s extglob + local on=true + fi + local clean="${orig//$'\e'[\[(]*([0-9;])[@-n]/}" + [[ "${on}" == true ]] && shopt -u extglob + echo "${clean}" +} + +# -------------------------------------------------------------------------------- # +# Core Functions # +# -------------------------------------------------------------------------------- # + +function install_prerequisites() +{ + local pip_update=('python' '-m' 'pip' 'install' '--quiet' '--upgrade' 'pip') + + stage 'Install Prerequisites' + + if [[ "${PYTHON_BASED_TOOL}" = true ]] ; then + # shellcheck disable=SC2310 + if ! errors=$(run_command "${pip_update[@]}"); then + fail "${pip_update[*]}" "${errors}" true + exit "${EXIT_VALUE}" + else + success "${pip_update[*]}" + fi + fi + + if [[ "${DOCKER_BASED_TOOL}" = true ]] ; then + # shellcheck disable=SC2310 + if ! errors=$(run_command "${INSTALL_COMMAND[@]}"); then + fail "${INSTALL_COMMAND[*]}" "${errors}" true + exit "${EXIT_VALUE}" + else + success "${INSTALL_COMMAND[*]}" + fi + else + if ! "${TEST_COMMAND[@]}" --help &> /dev/null; then + # shellcheck disable=SC2310 + if ! errors=$(run_command "${INSTALL_COMMAND[@]}"); then + fail "${INSTALL_COMMAND[*]}" "${errors}" true + exit "${EXIT_VALUE}" + else + success "${INSTALL_COMMAND[*]}" + fi + else + success "${TEST_COMMAND[*]} is already installed" + fi + fi + + if [[ "${INSTALL_REQUIREMENTS}" = true ]] ; then + while IFS= read -r filename + do + CMD=("pip" "install" "-r" "${filename}") + # shellcheck disable=SC2310 + if errors=$(run_command "${CMD[@]}" ); then + success "${CMD[*]}" + else + fail "${CMD[*]}" "${errors}" true + exit "${EXIT_VALUE}" + fi + done < <(find . -name 'requirements.txt' -type f -not -path "./.git/*" | sed 's|^./||' | sort -Vf || true) + fi +} + +function get_version_information() +{ + local output + + if [[ "${RUBY_GEM_BASED_TOOL}" = true ]] ; then + output=$(run_command gem list | grep "^${RUBY_GEM_NAME} " | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') + elif [[ "${PYTHON_BASED_TOOL}" = true ]] ; then + output=$(run_command "${TEST_COMMAND[@]}" --version) + output=$(echo "${output}" | tr -d '\n' | head -n 1 | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') + elif [[ "${DOCKER_BASED_TOOL}" = true ]] ; then + output=$(run_command docker run "${DOCKER_CONTAINER}" "${DOCKER_CONTAINER_SHORT}" --version) + output=$(echo "${output}" | tr -d '\n' | head -n 1 | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') + elif [[ "${PERL_BASED_TOOL}" = true ]] ; then + output=$(run_command "${TEST_COMMAND[@]}" -e 'print substr($^V, 1)') + else + output=$(run_command "${TEST_COMMAND[@]}" --version) + output=$(echo "${output}" | tr -d '\n' | head -n 1 | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') + fi + + # shellcheck disable=SC2181 + if [[ $? -ne 0 ]]; then + echo "Failed to get version information." + return 1 + fi + + VERSION="${output}" + BANNER="Run ${BANNER_NAME} (v${VERSION})" +} + +function check() +{ + local filename=$1 + + # shellcheck disable=SC2310 + if is_included "${filename}"; then + check_file "${filename}" + return + fi + + # shellcheck disable=SC2310 + if is_excluded "${filename}"; then + skip "${filename}" + return + fi + + if [[ "${#include_list[@]}" -ne 0 ]]; then + return + fi + check_file "${filename}" +} + +function scan_files() +{ + while IFS= read -r filename; do + if file -b "${filename}" | grep -qE "${FILE_TYPE_SEARCH_PATTERN}"; then + check "${filename}" + elif [[ "${filename}" =~ ${FILE_NAME_SEARCH_PATTERN} ]]; then + check "${filename}" + fi + done < <(find "${SCAN_ROOT}" -type f -not -path "./.git/*" | sed 's|^./||' | sort -Vf || true) +} + +function handle_parameters() +{ + stage "Parameters" + + if [[ -n "${REPORT_ONLY-}" ]] && [[ "${REPORT_ONLY}" = true ]]; then + REPORT_ONLY=true + echo " Report Only: ${cyan_text}true${reset}" + parameters=true + else + REPORT_ONLY=false + fi + + if [[ -n "${SHOW_ERRORS-}" ]] && [[ "${SHOW_ERRORS}" = false ]]; then + SHOW_ERRORS=false + echo " Show Errors: ${cyan_text}true${reset}" + parameters=true + else + SHOW_ERRORS=true + fi + + if [[ -n "${SHOW_SKIPPED-}" ]] && [[ "${SHOW_SKIPPED}" == true ]]; then + SHOW_SKIPPED=true + echo " Show Skipped: ${cyan_text}true${reset}" + parameters=true + else + SHOW_SKIPPED=false + fi + + if [[ -n "${INCLUDE_FILES-}" ]]; then + IFS=',' read -r -a include_list <<< "${INCLUDE_FILES}" + echo " Included Files: ${cyan_text}${INCLUDE_FILES}${reset}" + parameters=true + else + include_list=() + fi + + if [[ -n "${EXCLUDE_FILES-}" ]] && [[ "${#include_list[@]}" -eq 0 ]]; then + IFS=',' read -r -a exclude_list <<< "${EXCLUDE_FILES}" + echo " Excluded Files: ${cyan_text}${EXCLUDE_FILES}${reset}" + parameters=true + else + exclude_list=() + fi + + # shellcheck disable=SC2310 + if handle_non_standard_parameters; then + parameters=true + fi + + if [[ "${parameters}" != true ]]; then + echo " No parameters given" + fi +} + +function handle_color_parameters() +{ + if [[ -n "${NO_COLOR-}" ]]; then + if [[ "${NO_COLOR}" == true ]]; then + NO_COLOR=true + else + NO_COLOR=false + fi + else + NO_COLOR=false + fi +} + +function footer() +{ + stage 'Report' + echo " ${bold_text}Total${reset}: ${file_count}, ${bold_text}${green_text}OK${reset}: ${ok_count}, ${bold_text}${red_text}Failed${reset}: ${fail_count}, ${bold_text}${yellow_text}Skipped${reset}: ${skip_count}" + stage 'Complete' +} + +function setup() +{ + export TERM=xterm + + handle_color_parameters + + screen_width=0 + # shellcheck disable=SC2034 + bold_text='' + # shellcheck disable=SC2034 + reset='' + # shellcheck disable=SC2034 + black_text='' + # shellcheck disable=SC2034 + red_text='' + # shellcheck disable=SC2034 + green_text='' + # shellcheck disable=SC2034 + yellow_text='' + # shellcheck disable=SC2034 + blue_text='' + # shellcheck disable=SC2034 + magenta_text='' + # shellcheck disable=SC2034 + cyan_text='' + # shellcheck disable=SC2034 + white_text='' + + if [[ "${NO_COLOR}" == false ]]; then + screen_width=$(tput cols) + screen_width=$((screen_width - 2)) + + # shellcheck disable=SC2034 + bold_text=$(tput bold) + # shellcheck disable=SC2034 + reset=$(tput sgr0) + # shellcheck disable=SC2034 + black_text=$(tput setaf 0) + # shellcheck disable=SC2034 + red_text=$(tput setaf 1) + # shellcheck disable=SC2034 + green_text=$(tput setaf 2) + # shellcheck disable=SC2034 + yellow_text=$(tput setaf 3) + # shellcheck disable=SC2034 + blue_text=$(tput setaf 4) + # shellcheck disable=SC2034 + magenta_text=$(tput setaf 5) + # shellcheck disable=SC2034 + cyan_text=$(tput setaf 6) + # shellcheck disable=SC2034 + white_text=$(tput setaf 7) + fi + + (( screen_width < 140 )) && screen_width=140 + file_count=0 + ok_count=0 + fail_count=0 + skip_count=0 + parameters=false +} + +# -------------------------------------------------------------------------------- # +# Main # +# -------------------------------------------------------------------------------- # + +setup +handle_parameters +install_prerequisites +get_version_information +stage "${BANNER}" +scan_files +footer + +[[ "${REPORT_ONLY}" == true ]] && EXIT_VALUE=0 + +exit "${EXIT_VALUE}"