diff --git a/.github/actions/generate-jira-pr-report/action.yml b/.github/actions/generate-jira-pr-report/action.yml new file mode 100644 index 0000000..04693f6 --- /dev/null +++ b/.github/actions/generate-jira-pr-report/action.yml @@ -0,0 +1,66 @@ +name: 'Generate Jira and Pull-request report' +description: 'Generate a report of all commits, pr-authors, pr-approver and jira reference for a period' +inputs: + # The Following environment variables must be set in your GitHub action + # before using this composite + # env: + # KOSLI_ORG: kosli-public + # KOSLI_API_TOKEN: "${{ secrets.KOSLI_API_TOKEN }}" + # KOSLI_CLI_VERSION: 2.11.11 + result-dir: + description: 'Directory where result is stored' + required: true + kosli-source-flow: + description: 'Kosli flow that tracks source specific attestations that include PR and Jira issues' + required: true + start-date: + description: 'Start date' + required: true + end-date: + description: 'End date' + required: true + +outputs: + result-csv-file: + description: "Name of file that contains the result" + value: ${{ steps.generate-report.outputs.result-csv-file }} + result-csv-path: + description: "Path to csv-file that contains the result" + value: ${{ steps.generate-report.outputs.result-csv-path }} + +runs: + using: "composite" + steps: + - name: Setup Kosli cli + uses: kosli-dev/setup-cli-action@v2 + with: + version: + ${{ env.KOSLI_CLI_VERSION }} + + - name: Generate report + id: generate-report + shell: bash + run: | + mkdir -p ${{ inputs.result-dir }} + ${GITHUB_ACTION_PATH}/scripts/generate-jira-pr-report.bash ${{ inputs.result-dir }} ${{ inputs.kosli-source-flow }} ${{ inputs.start-date }} ${{ inputs.end-date }} + echo "result-csv-file=source.csv" >> $GITHUB_OUTPUT + echo "result-csv-path=${{ inputs.result-dir }}/source.csv" >> $GITHUB_OUTPUT + + - name: Format and add CSV to summary + shell: bash + run: | + echo "Reports from and including ${{ inputs.start-date }} up to and not including ${{ inputs.end-date }}" >> $GITHUB_STEP_SUMMARY + # Read the CSV file and convert to Markdown table + { + # Read the header and format it + head -n 1 ${{ steps.generate-report.outputs.result-csv-path }} | sed 's/,/|/g; s/^/|/; s/$/|/' + + # Add the separator line + head -n 1 ${{ steps.generate-report.outputs.result-csv-path }} | sed 's/[^,]*/-/g; s/,/|/g; s/^/|/; s/$/|/' + + # Read the rest of the file and format it + tail -n +2 ${{ steps.generate-report.outputs.result-csv-path }} | sed 's/,/|/g; s/^/|/; s/$/|/' + } > formatted_table.md + + # Add the formatted table to the step summary + cat formatted_table.md >> $GITHUB_STEP_SUMMARY diff --git a/.github/actions/generate-jira-pr-report/scripts/generate-jira-pr-report.bash b/.github/actions/generate-jira-pr-report/scripts/generate-jira-pr-report.bash new file mode 100755 index 0000000..a8d961b --- /dev/null +++ b/.github/actions/generate-jira-pr-report/scripts/generate-jira-pr-report.bash @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +set -Eeu + +SCRIPT_NAME=generate-jira-pr-report.bash +ROOT_DIR=$(dirname $(readlink -f $0)) +RESULT_DIR="" +KOSLI_FLOW="" +START_DATE="" +END_DATE="" + +source ${ROOT_DIR}/../../../../scripts/lib-kosli.sh + +function print_help +{ + cat < [start-date] [end-date] + +Create a csv report documenting all commits, pull-requests +and jira issue references for a period. + +The result end up in ${SOURCE_REPORT_FILE} + +Required date format: YYYY-MM-DD + +The end-date is not included, so the following +will give you everything that happened in April 2025 + +$SCRIPT_NAME /tmp/audit-report jira-example-source 2025-04-01 2025-05-01 + +Options are: + -h Print this help menu +EOF +} + +function check_arguments +{ + while getopts "h" opt; do + case $opt in + h) + print_help + exit 1 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac + done + + # Remove options from command line + shift $((OPTIND-1)) + + if [ $# -ne 4 ]; then + echo "Missing arguments" + exit 1 + fi + RESULT_DIR=$1; shift + KOSLI_FLOW=$1; shift + START_DATE=$1; shift + END_DATE=$1; shift + + SOURCE_REPORT_DIR=${RESULT_DIR}/source + SOURCE_REPORT_FILE=${RESULT_DIR}/source.csv +} + +main() +{ + check_arguments "$@" + commits=$(git log --format="%H %ad" --date=format:"%Y-%m-%d" --after="${START_DATE}T00:00:00" --before="${END_DATE}T00:00:00") + mkdir -p ${SOURCE_REPORT_DIR} + echo "commit,date,commit-author,pr-approver,jira-issue" > ${SOURCE_REPORT_FILE} + + while IFS=' ' read -r sha date; do + mkdir -p ${SOURCE_REPORT_DIR}/${sha} + get_attestation_from_trail ${KOSLI_FLOW} ${sha} pull-request | jq . > ${SOURCE_REPORT_DIR}/${sha}/pull-request.json || true + get_attestation_from_trail ${KOSLI_FLOW} ${sha} work-reference | jq . > ${SOURCE_REPORT_DIR}/${sha}/work-reference.json || true + + echo ${sha} | tr '\n' ',' >> ${SOURCE_REPORT_FILE} + echo ${date} | tr '\n' ',' >> ${SOURCE_REPORT_FILE} + + jq -r '.[].git_commit_info.author' ${SOURCE_REPORT_DIR}/${sha}/pull-request.json | tr '\n' ',' >> ${SOURCE_REPORT_FILE} + jq -r '.[].pull_requests[].approvers[]' ${SOURCE_REPORT_DIR}/${sha}/pull-request.json | uniq | tr '\n' ',' >> ${SOURCE_REPORT_FILE} + + jq -r '.[] | .jira_results[] | select(.issue_exists == true) | .issue_id' ${SOURCE_REPORT_DIR}/${sha}/work-reference.json | tr '\n' ',' >> ${SOURCE_REPORT_FILE} + + echo >> ${SOURCE_REPORT_FILE} + done <<< "$commits" +} + +main "$@" diff --git a/.github/actions/generate-periodic-reports/action.yml b/.github/actions/generate-periodic-reports/action.yml new file mode 100644 index 0000000..c22001a --- /dev/null +++ b/.github/actions/generate-periodic-reports/action.yml @@ -0,0 +1,63 @@ +name: 'Generate Periodic Reports' + +description: 'Generates a selection of periodic reports. + Currently it only has Jira and PR report' + +inputs: + start-date: + description: 'Start date for the report (optional)' + required: false + end-date: + description: 'End date for the report (optional)' + required: false + kosli-source-flow: + description: 'Kosli source flow name. Used for Jira and PR report' + required: true + +outputs: + result-jira-pr-csv-file: + description: 'Name of the jira and PR result CSV file' + value: ${{ steps.jira-pr-report.outputs.result-csv-file }} + result-jira-pr-artifact-upload-name: + description: 'Name of the artifact used in upload-artifact action' + value: source-report-${{ github.run_id }} + +runs: + using: "composite" + steps: + - name: Wait for Random Delay + if: ${{ github.event_name == 'schedule' }} + shell: bash + run: | + RANDOM_DELAY=$((RANDOM % 3600)) + sleep $RANDOM_DELAY + + - name: Calculate Dates + id: calculate-dates + shell: bash + run: | + if [ "${{ github.event_name }}" == "schedule" ]; then + START_DATE=$(date -d "$(date +'%Y-%m-01') -1 month" +'%Y-%m-01') + END_DATE=$(date +'%Y-%m-01') + else + START_DATE="${{ inputs.start-date }}" + END_DATE="${{ inputs.end-date }}" + fi + echo "start-date=$START_DATE" >> $GITHUB_ENV + echo "end-date=$END_DATE" >> $GITHUB_ENV + + - name: Generate Jira PR report + id: jira-pr-report + uses: ./.github/actions/generate-jira-pr-report@main + with: + result-dir: /tmp/audit-reports + kosli-source-flow: ${{ inputs.kosli-source-flow }} + start-date: ${{ env.start-date }} + end-date: ${{ env.end-date }} + + - uses: actions/upload-artifact@v4 + with: + name: source-report-${{ github.run_id }} + path: ${{ steps.jira-pr-report.outputs.result-csv-path }} + overwrite: true + \ No newline at end of file diff --git a/.github/workflows/attest-source-controls.yml b/.github/workflows/attest-source-controls.yml index c6c671f..3eeb0d9 100644 --- a/.github/workflows/attest-source-controls.yml +++ b/.github/workflows/attest-source-controls.yml @@ -49,6 +49,7 @@ jobs: jira-api-token: ${{ env.JIRA_API_TOKEN }} jira-project-key: ${{ env.JIRA_PROJECT_KEY }} jira-issue-fields: "summary,description" + allow-only-branch-match: true report-to-kosli: ${{ github.ref == 'refs/heads/main' }} pull-request: diff --git a/.github/workflows/build-backend.yml b/.github/workflows/build-backend.yml index e65dc4e..93ab868 100644 --- a/.github/workflows/build-backend.yml +++ b/.github/workflows/build-backend.yml @@ -49,6 +49,7 @@ jobs: jira-api-token: ${{ env.JIRA_API_TOKEN }} jira-project-key: ${{ env.JIRA_PROJECT_KEY }} jira-issue-fields: "summary,description" + allow-only-branch-match: true report-to-kosli: ${{ github.ref == 'refs/heads/main' }} pull-request: diff --git a/.github/workflows/build-frontend.yml b/.github/workflows/build-frontend.yml index 01a9a69..8c5e837 100644 --- a/.github/workflows/build-frontend.yml +++ b/.github/workflows/build-frontend.yml @@ -49,6 +49,7 @@ jobs: jira-api-token: ${{ env.JIRA_API_TOKEN }} jira-project-key: ${{ env.JIRA_PROJECT_KEY }} jira-issue-fields: "summary,description" + allow-only-branch-match: true report-to-kosli: ${{ github.ref == 'refs/heads/main' }} pull-request: diff --git a/.github/workflows/generate-monthly-reports.yml b/.github/workflows/generate-monthly-reports.yml new file mode 100644 index 0000000..5bf5069 --- /dev/null +++ b/.github/workflows/generate-monthly-reports.yml @@ -0,0 +1,40 @@ +name: Generate monthly reports + +on: + schedule: + - cron: '0 0 1 * *' # Triggers at midnight on the 1st of every month. + workflow_dispatch: + inputs: + start-date: + description: 'Start date (YYYY-MM-DD)' + required: true + end-date: + description: 'End date (YYYY-MM-DD)' + required: true + +env: + KOSLI_ORG: "${{ vars.KOSLI_ORG }}" + KOSLI_API_TOKEN: "${{ secrets.KOSLI_API_TOKEN }}" + KOSLI_CLI_VERSION: "${{ vars.KOSLI_CLI_VERSION }}" + + +jobs: + generate-reports: + name: Generate periodic reports + runs-on: ubuntu-latest + outputs: + result-jira-pr-csv-file: ${{ steps.generate-periodic-reports.outputs.result-jira-pr-csv-file }} + result-jira-pr-artifact-upload-name: ${{ steps.generate-periodic-reports.outputs.result-jira-pr-artifact-upload-name }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate reports + id: generate-periodic-reports + uses: ./.github/actions/generate-periodic-reports@main + with: + start-date: ${{ github.event.inputs.start-date }} + end-date: ${{ github.event.inputs.end-date }} + kosli-source-flow: jira-example-source +