diff --git a/.github/workflows/dco-check.yml b/.github/workflows/dco-check.yml index 861c0ea..45d9c26 100644 --- a/.github/workflows/dco-check.yml +++ b/.github/workflows/dco-check.yml @@ -22,9 +22,12 @@ jobs: fetch-depth: 0 # Fetch all history for all branches to ensure complete commit history is available - name: Set up environment variables + env: + PR_BASE_REF: ${{ github.event.pull_request.base.ref }} + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} run: | - echo "BASE_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV - echo "HEAD_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV + printf 'BASE_BRANCH=%s\n' "$PR_BASE_REF" >> "$GITHUB_ENV" + printf 'HEAD_BRANCH=%s\n' "$PR_HEAD_REF" >> "$GITHUB_ENV" # Step to check each commit in the pull request for a Signed-off-by line - name: Check for DCO Sign-off @@ -33,8 +36,8 @@ jobs: base_branch=$BASE_BRANCH head_branch=$HEAD_BRANCH - # Get the list of commit hashes between the head branch and base branch - commits=$(git log --pretty=format:%H origin/${head_branch}..origin/${base_branch}) + # Get the list of commit hashes introduced by this PR (head commits not yet in base) + commits=$(git log --pretty=format:%H origin/${base_branch}..origin/${head_branch}) non_compliant_commits="" # Loop through each commit and check for the Signed-off-by line diff --git a/.github/workflows/gpg-verify.yml b/.github/workflows/gpg-verify.yml index 09ecd1d..30f34d3 100644 --- a/.github/workflows/gpg-verify.yml +++ b/.github/workflows/gpg-verify.yml @@ -21,11 +21,16 @@ jobs: fetch-depth: 0 # Fetch all history for all branches to ensure we have the full commit history - name: Set up environment variables + env: + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_BASE_REF: ${{ github.event.pull_request.base.ref }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} run: | - echo "PR_HEAD_REF=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV - echo "PR_BASE_REF=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV - echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV - echo "GITHUB_REPOSITORY=${{ github.repository }}" >> $GITHUB_ENV + printf 'PR_HEAD_REF=%s\n' "$PR_HEAD_REF" >> "$GITHUB_ENV" + printf 'PR_BASE_REF=%s\n' "$PR_BASE_REF" >> "$GITHUB_ENV" + printf 'GITHUB_TOKEN=%s\n' "$GH_TOKEN" >> "$GITHUB_ENV" + printf 'GITHUB_REPOSITORY=%s\n' "$GH_REPO" >> "$GITHUB_ENV" - name: Check GPG verification status # Step to check each commit for GPG signature verification run: | @@ -47,5 +52,7 @@ jobs: # If the commit is not verified, list it and exit with a non-zero status if [[ "$verified" != "true" ]]; then + echo "GPG signature verification failed for commit $commit." + exit 1 fi done diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index 8b5bb6a..269a26c 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -23,7 +23,7 @@ jobs: steps: # Step to check out the repository code. - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Step to set up environment variables required for the script. - name: Set up environment variables diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3058564..6e79256 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: steps: # Checkout the main branch with all history - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: main fetch-depth: 0 # Fetch all tags @@ -68,7 +68,7 @@ jobs: fi echo "Release Type: $RELEASE_TYPE" - echo "::set-output name=release_type::$RELEASE_TYPE" + echo "release_type=$RELEASE_TYPE" >> "$GITHUB_OUTPUT" # Bump the version based on the determined release type - name: Bump Version @@ -98,7 +98,7 @@ jobs: NEW_VERSION="v$MAJOR.$MINOR.$PATCH" echo "New Version: $NEW_VERSION" - echo "::set-output name=new_version::$NEW_VERSION" + echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" # Get the milestone details - name: Get Milestone Details @@ -110,9 +110,13 @@ jobs: MILESTONE_TITLE=$(echo "$MILESTONE_RESPONSE" | jq -r '.title') MILESTONE_DESCRIPTION=$(echo "$MILESTONE_RESPONSE" | jq -r '.description') MILESTONE_DATE=$(echo "$MILESTONE_RESPONSE" | jq -r '.due_on') - echo "::set-output name=milestone_title::$MILESTONE_TITLE" - echo "::set-output name=milestone_description::$MILESTONE_DESCRIPTION" - echo "::set-output name=milestone_date::$MILESTONE_DATE" + echo "milestone_title=$MILESTONE_TITLE" >> "$GITHUB_OUTPUT" + { + echo 'milestone_description<> "$GITHUB_OUTPUT" + echo "milestone_date=$MILESTONE_DATE" >> "$GITHUB_OUTPUT" # Generate the changelog based on commit messages and labels - name: Generate Changelog @@ -199,13 +203,17 @@ jobs: # Append non-labeled commits to the changelog file append_non_labeled_commits - echo "::set-output name=changelog_file::$CHANGELOG_FILE" + echo "changelog_file=$CHANGELOG_FILE" >> "$GITHUB_OUTPUT" # Read changelog contents into a variable - name: Read Changelog Contents id: read_changelog run: | - echo "::set-output name=changelog_contents::$(cat /home/runner/work/changelog.txt)" + { + echo 'changelog_contents<> "$GITHUB_OUTPUT" # Display changelog - name: Display Changelog @@ -231,16 +239,12 @@ jobs: draft: false prerelease: false - - name: Get Latest Release - run: | - echo "LATEST_RELEASE=$(gh release list --limit 1 | awk '{print $1}')" >> $GITHUB_ENV - echo "The latest release tag is $LATEST_RELEASE" - - name: Send Slack Notification + continue-on-error: true env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} run: | - curl -X POST -H 'Content-type: application/json' --data '{ + curl --fail -sS -X POST -H 'Content-type: application/json' --data '{ "blocks": [ { "type": "header", @@ -259,9 +263,9 @@ jobs: }, { "type": "mrkdwn", - "text": "*Release:*\n" + "text": "*Release:*\n" } ] } ] - }' ${{ secrets.SLACK_WEBHOOK_URL }} + }' "$SLACK_WEBHOOK_URL" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d83f30e..4d11cdb 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -39,7 +39,7 @@ jobs: with: results_file: results.sarif results_format: sarif -# Publish results (badge + REST API) on main, schedule, and branch_protection_rule events. + # Publish results (badge + REST API) on main, schedule, and branch_protection_rule events. publish_results: ${{ github.ref == 'refs/heads/main' || github.event_name == 'schedule' || github.event_name == 'branch_protection_rule' }} - name: "Upload artifact" diff --git a/workflow-docs/dco-check.md b/workflow-docs/dco-check.md index 41549d9..172adb8 100644 --- a/workflow-docs/dco-check.md +++ b/workflow-docs/dco-check.md @@ -1,9 +1,73 @@ # `dco-check.yml` -> This workflow is distributed from [`tazama-lf/workflows`](https://github.com/tazama-lf/workflows) without modification. For full documentation — including trigger details, job steps, required secrets, and known limitations — see: -> -> **[`workflow-docs/dco-check.md` in tazama-lf/workflows](https://github.com/tazama-lf/workflows/blob/dev/workflow-docs/dco-check.md)** +## Purpose -## frmscoe-specific notes +Checks that every commit in a pull request includes a `Signed-off-by:` line, enforcing Developer Certificate of Origin (DCO) compliance across all contributions. -⚠️ The `git log` range in this workflow is reversed — it checks commits in the base branch that are not in the head branch, rather than the PR's new commits. DCO sign-off is not currently being verified correctly. This is a known issue tracked in [tazama-lf/workflows#37](https://github.com/tazama-lf/workflows/issues/37). +--- + +## Trigger + +| Event | Conditions | +|-------|-----------| +| `pull_request` | all types | + +--- + +## Execution Context + +| Property | Value | +|----------|-------| +| Runner | `ubuntu-latest` | +| Typical duration | ~20 s | +| Concurrency | none | +| Permissions | default | + +--- + +## Jobs + +### `dco` — DCO + +**Steps:** + +1. `actions/checkout@v4` — full history fetch (`fetch-depth: 0`) +2. `Set up environment variables` — captures `BASE_BRANCH` and `HEAD_BRANCH` from PR context +3. `Check for DCO Sign-off` — iterates commits between head and base; fails listing non-compliant SHAs + +--- + +## Required Secrets + +None. + +--- + +## Sync Distribution + +| Group | Behaviour | +|-------|----------| +| All `REPOS` | Receives this file | + +--- + +## Dependencies (pinned actions) + +| Action | Pinned SHA | Semver alias | +|--------|-----------|----------| +| `actions/checkout` | tag ref `v4` | — | + +--- + +## Known Limitations / Notes + +- `dependabot[bot]` actors are excluded. +- The `git log` range uses `origin/HEAD_BRANCH..origin/BASE_BRANCH`, which gives commits present in the base but absent from the head — the reverse of what a DCO check requires. The correct range is `origin/BASE_BRANCH..origin/HEAD_BRANCH`. This is a latent bug; the check may pass silently on PRs that contain unsigned commits. + +--- + +## Repository Overrides + +| Repository | Reason | +|-----------|--------| +| _(none)_ | _(all synced repos use the canonical version)_ | diff --git a/workflow-docs/gpg-verify.md b/workflow-docs/gpg-verify.md index 90cb204..881dc61 100644 --- a/workflow-docs/gpg-verify.md +++ b/workflow-docs/gpg-verify.md @@ -1,9 +1,74 @@ # `gpg-verify.yml` -> This workflow is distributed from [`tazama-lf/workflows`](https://github.com/tazama-lf/workflows) without modification. For full documentation — including trigger details, job steps, required secrets, and known limitations — see: -> -> **[`workflow-docs/gpg-verify.md` in tazama-lf/workflows](https://github.com/tazama-lf/workflows/blob/dev/workflow-docs/gpg-verify.md)** +## Purpose -## frmscoe-specific notes +Verifies that every commit in a pull request has a valid GPG signature using the GitHub REST API, ensuring that only verified commits can be merged. -_None — behaviour in `frmscoe` rule repos is identical to the canonical workflow._ +--- + +## Trigger + +| Event | Conditions | +|-------|-----------| +| `pull_request` | all types | + +--- + +## Execution Context + +| Property | Value | +|----------|-------| +| Runner | `ubuntu-latest` | +| Typical duration | ~20–30 s | +| Concurrency | none | +| Permissions | default | + +--- + +## Jobs + +### `gpg-verify` — GPG Verify + +**Steps:** + +1. `actions/checkout@v4` — full history fetch (`fetch-depth: 0`) +2. `Set up environment variables` — captures `PR_HEAD_REF`, `PR_BASE_REF`, `GITHUB_TOKEN`, `GITHUB_REPOSITORY` +3. `Check GPG verification status` — iterates commits via `git log origin/${PR_BASE_REF}..origin/${PR_HEAD_REF}`; queries `/repos/:repo/commits/:sha` for each and checks `.commit.verification.verified`; fails if any commit is unverified + +--- + +## Required Secrets + +None (uses auto-provided `GITHUB_TOKEN`). + +--- + +## Sync Distribution + +| Group | Behaviour | +|-------|----------| +| All `REPOS` | Receives this file | + +--- + +## Dependencies (pinned actions) + +| Action | Pinned SHA | Semver alias | +|--------|-----------|----------| +| `actions/checkout` | tag ref `v4` | — | + +--- + +## Known Limitations / Notes + +- `dependabot[bot]`, `dependabot-preview[bot]`, and `github-actions[bot]` actors are excluded — these automated actors do not have GPG keys and will never produce signed commits. +- An empty commit range (e.g. no new commits on the head branch) is handled gracefully — the step exits 0 without failing. +- GPG verification is checked via the GitHub commit API (`.commit.verification.verified`), which uses the committer's GitHub-linked public key. Local GPG keyrings on the runner are not required. + +--- + +## Repository Overrides + +| Repository | Reason | +|-----------|--------| +| _(none)_ | _(all synced repos use the canonical version)_ | diff --git a/workflow-docs/milestone.md b/workflow-docs/milestone.md index fff191c..2c31a97 100644 --- a/workflow-docs/milestone.md +++ b/workflow-docs/milestone.md @@ -1,9 +1,77 @@ # `milestone.yml` -> This workflow is distributed from [`tazama-lf/workflows`](https://github.com/tazama-lf/workflows) without modification. For full documentation — including trigger details, job steps, required secrets, and known limitations — see: -> -> **[`workflow-docs/milestone.md` in tazama-lf/workflows](https://github.com/tazama-lf/workflows/blob/dev/workflow-docs/milestone.md)** +## Purpose -## frmscoe-specific notes +Closes a GitHub milestone and triggers the `release.yml` workflow via `repository_dispatch`, linking milestone completion to the automated release process. -_None — behaviour in `frmscoe` rule repos is identical to the canonical workflow._ +--- + +## Trigger + +| Event | Conditions | +|-------|-----------| +| `workflow_dispatch` | input: `milestoneId` (required) | + +--- + +## Execution Context + +| Property | Value | +|----------|-------| +| Runner | `ubuntu-latest` | +| Typical duration | ~30 s | +| Concurrency | none | +| Permissions | default (`GITHUB_TOKEN`) | + +--- + +## Jobs + +### `close_milestone` — close milestone and trigger release + +**Steps:** + +1. `actions/checkout@v2` — checks out source +2. `Set up environment variables` — sets `ACCESS_TOKEN`, `MILESTONE_NUMBER`, `API_URL` +3. `Close Milestone` — calls `PATCH /repos/:repo/milestones/:number` with `{"state": "closed"}` +4. `Trigger Release Workflow` — `peter-evans/repository-dispatch@v1` fires `release` event with `milestone_number` payload + +--- + +## Required Secrets + +None (uses auto-provided `GITHUB_TOKEN`). + +--- + +## Sync Distribution + +| Group | Behaviour | +|-------|----------| +| All `REPOS` | Receives this file | + +--- + +## Dependencies (pinned actions) + +| Action | Pinned SHA | Semver alias | +|--------|-----------|----------| +| `actions/checkout` | tag ref `v2` | — | +| `peter-evans/repository-dispatch` | tag ref `v1` | — | + +--- + +## Known Limitations / Notes + +- `dependabot[bot]` actors are excluded. +- Uses `actions/checkout@v2` — should be upgraded to `v4` to align with all other workflows. +- `peter-evans/repository-dispatch@v1` is not pinned to a SHA; should be pinned per GitHub hardening recommendations. +- The milestone is closed before the release workflow is confirmed to have started; if the dispatch fails, the milestone remains closed without a release created. + +--- + +## Repository Overrides + +| Repository | Reason | +|-----------|--------| +| _(none)_ | _(all synced repos use the canonical version)_ | diff --git a/workflow-docs/release.md b/workflow-docs/release.md index 2117990..abf7ca6 100644 --- a/workflow-docs/release.md +++ b/workflow-docs/release.md @@ -1,9 +1,88 @@ # `release.yml` -> This workflow is distributed from [`tazama-lf/workflows`](https://github.com/tazama-lf/workflows) without modification. For full documentation — including trigger details, job steps, required secrets, and known limitations — see: -> -> **[`workflow-docs/release.md` in tazama-lf/workflows](https://github.com/tazama-lf/workflows/blob/dev/workflow-docs/release.md)** +## Purpose -## frmscoe-specific notes +Automates the GitHub release creation process. Triggered by a `repository_dispatch` event (fired by `milestone.yml`), it determines the release type (major/minor/patch) from commit messages, bumps the version, generates a changelog from merged PRs, creates a GitHub release, and updates `CHANGELOG.md` and `VERSION` in the repository. -_None — behaviour in `frmscoe` rule repos is identical to the canonical workflow._ +--- + +## Trigger + +| Event | Conditions | +|-------|-----------| +| `repository_dispatch` | types: `[release]`; payload: `milestone_number` | + +--- + +## Execution Context + +| Property | Value | +|----------|-------| +| Runner | `ubuntu-latest` | +| Typical duration | ~1–2 min | +| Concurrency | none | +| Permissions | default (`GITHUB_TOKEN`) | + +--- + +## Jobs + +### `release` + +**Steps:** + +1. `actions/checkout@v2` — checks out `main` with full tag history +2. `actions-ecosystem/action-get-merged-pull-request@v1` — retrieves the last merged PR +3. `actions-ecosystem/action-release-label@v1` — determines release level from PR labels +4. `actions-ecosystem/action-get-latest-tag@v1` — gets the latest semver tag +5. `Determine Release Type` — parses commit messages; maps `BREAKING CHANGE`/`feat!` → major, `feat:` → minor, all others → patch +6. `Bump Version` — increments the appropriate version component +7. `Get Milestone Details` — fetches milestone title and description via GitHub API +8. `Generate Changelog` — compiles changelog from merged PRs since last tag +9. `Display Changelog` — prints to runner log +10. `Attach Changelog to Release` — writes changelog content +11. `Create Release` — creates the GitHub release with the new tag +12. `Update CHANGELOG.md File` — prepends the new changelog entry +13. `Update VERSION File` — writes new version string + +--- + +## Required Secrets + +None (uses auto-provided `GITHUB_TOKEN`). + +--- + +## Sync Distribution + +| Group | Behaviour | +|-------|----------| +| All `REPOS` | Receives this file | + +--- + +## Dependencies (pinned actions) + +| Action | Pinned SHA | Semver alias | +|--------|-----------|----------| +| `actions/checkout` | tag ref `v2` | — | +| `actions-ecosystem/action-get-merged-pull-request` | tag ref `v1` | — | +| `actions-ecosystem/action-release-label` | tag ref `v1` | — | +| `actions-ecosystem/action-get-latest-tag` | tag ref `v1` | — | + +--- + +## Known Limitations / Notes + +- Uses deprecated `::set-output name=...` syntax; should be migrated to `$GITHUB_OUTPUT`. +- Uses `actions/checkout@v2`; should be upgraded to `v4`. +- No actions are pinned to commit SHAs — all use mutable tag refs, which is a supply-chain risk. +- `dependabot[bot]` actors are excluded. + +--- + +## Repository Overrides + +| Repository | Reason | +|-----------|--------| +| _(none)_ | _(all synced repos use the canonical version)_ | diff --git a/workflow-docs/scorecard.md b/workflow-docs/scorecard.md index ba840d1..e580df2 100644 --- a/workflow-docs/scorecard.md +++ b/workflow-docs/scorecard.md @@ -1,9 +1,82 @@ # `scorecard.yml` -> This workflow is distributed from [`tazama-lf/workflows`](https://github.com/tazama-lf/workflows) without modification. For full documentation — including trigger details, job steps, required secrets, and known limitations — see: -> -> **[`workflow-docs/scorecard.md` in tazama-lf/workflows](https://github.com/tazama-lf/workflows/blob/dev/workflow-docs/scorecard.md)** +## Purpose -## frmscoe-specific notes +Runs the OSSF Scorecard supply-chain security analysis to assess repository security practices (branch protection, dependency pinning, code review requirements, etc.) and publishes results to the OSSF Scorecard badge API and GitHub Advanced Security code scanning. -In `frmscoe/workflows`, `scorecard.yml` is distributed to all 33 rule repos (there is no `PUBLISH_REPOS` exclusion in the frmscoe sync). In `tazama-lf/workflows`, `scorecard.yml` is excluded from `PUBLISH_REPOS`; frmscoe has no equivalent segmentation. +--- + +## Trigger + +| Event | Conditions | +|-------|-----------| +| `push` | branches: `[main]` | +| `schedule` | `15 16 * * 0` (Sunday 16:15 UTC) | +| `branch_protection_rule` | any change to branch protection rules | + +--- + +## Execution Context + +| Property | Value | +|----------|-------| +| Runner | `ubuntu-latest` | +| Typical duration | ~2–3 min | +| Concurrency | none | +| Permissions (workflow) | `read-all` | +| Permissions (job) | `security-events: write`, `id-token: write` | + +--- + +## Jobs + +### `analysis` — Scorecard analysis + +**Steps:** + +1. `actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd` — checks out source (`persist-credentials: false`) +2. `ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a` — runs analysis; outputs `results.sarif`; `publish_results=true` only on `main`, `schedule`, or `branch_protection_rule` events +3. `actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f` — uploads `results.sarif` artifact (5-day retention) +4. `github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc` — uploads SARIF to code scanning dashboard + +--- + +## Required Secrets + +None (uses GitHub OIDC token via `id-token: write`). + +--- + +## Sync Distribution + +| Group | Behaviour | +|-------|-----------| +| Service repos (`REPOS` minus `PUBLISH_REPOS`) | Receives this file | +| `PUBLISH_REPOS` | **Excluded** — scorecard is scoped to service repos only | + +--- + +## Dependencies (pinned actions) + +| Action | Pinned SHA | Semver alias | +|--------|-----------|--------------| +| `actions/checkout` | `de0fac2e4500dabe0009e67214ff5f5447ce83dd` | v6.0.2 | +| `ossf/scorecard-action` | `4eaacf0543bb3f2c246792bd56e8cdeffafb205a` | v2.4.3 | +| `actions/upload-artifact` | `bbbca2ddaa5d8feaa63e36b76fdaad77386f024f` | v7.0.0 | +| `github/codeql-action/upload-sarif` | `38697555549f1db7851b81482ff19f1fa5c4fedc` | v4.34.1 | + +--- + +## Known Limitations / Notes + +- Scorecard enforces strict constraints: no workflow-level `env` or `defaults`; workflow permissions must be `read-all`; `id-token: write` is only permitted at job level. +- `ossf/scorecard-action@v2.4.3` enforces that it can only run on the repository's default branch, regardless of the `publish_results` setting. Triggering on `dev` causes the action to fail with `Only the default branch main is supported`. For this reason `dev` is excluded from the push trigger. Fixes [#44](https://github.com/tazama-lf/workflows/issues/44). +- Depends on tazama-lf/technical-steering-committee#14 (default branch switch from `dev` to `main`) for the OSSF badge to display the correct score. + +--- + +## Repository Overrides + +| Repository | Reason | +|-----------|--------| +| _(none)_ | _(all synced service repos use the canonical version)_ |