Skip to content
Merged

175 #179

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
branches: [ "main" ]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: write
pull-requests: write
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
tags:
- "v*"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

jobs:
checks:
uses: ./.github/workflows/reusable-ci.yml
Expand Down
90 changes: 71 additions & 19 deletions .github/workflows/reusable-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,12 @@
default: true

jobs:
ci:
msrv:
runs-on: ubuntu-latest
env:
CARGO_LOCKED: "true"

outputs:
version: ${{ steps.msrv.outputs.msrv }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: true # оставляем токен в origin, чтобы git push работал

- name: Read MSRV from Cargo.toml
id: msrv
Expand All @@ -40,187 +36,243 @@
echo "msrv=${RV}" >> "$GITHUB_OUTPUT"
echo "Using MSRV: $RV"

- name: Install Rust (${{ steps.msrv.outputs.msrv }})
ci:
runs-on: ubuntu-latest
needs: msrv
strategy:
fail-fast: false
matrix:
rust:
- ${{ needs.msrv.outputs.version }}
- stable
env:
CARGO_LOCKED: "true"

steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: true # оставляем токен в origin, чтобы git push работал

- name: Install Rust (${{ matrix.rust }})
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ steps.msrv.outputs.msrv }}
toolchain: ${{ matrix.rust }}
components: clippy

- name: Install nightly rustfmt
uses: dtolnay/rust-toolchain@v1
with:
toolchain: nightly
components: rustfmt

- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}

- name: Verify lockfile is committed
shell: bash
run: |
set -euo pipefail
if [ ! -f Cargo.lock ]; then
echo "CARGO_LOCKED=1 but Cargo.lock is missing. Commit it or drop CARGO_LOCKED."
exit 1
fi

# ---------- README handling ----------
- name: Build (may regenerate README to temp)
if: matrix.rust == needs.msrv.outputs.version
shell: bash
env:
TZ: UTC
LC_ALL: C.UTF-8
NO_COLOR: "1"
CARGO_TERM_COLOR: never
SOURCE_DATE_EPOCH: "0"
run: |
set -euo pipefail
tmp="$(mktemp)"
cargo +${{ steps.msrv.outputs.msrv }} build --workspace -q || cargo +${{ steps.msrv.outputs.msrv }} build -q
cargo +${{ matrix.rust }} build --workspace -q || cargo +${{ matrix.rust }} build -q
if [ -f README.md ]; then
cp -f README.md "$tmp"
last_byte="$(tail -c1 "$tmp" 2>/dev/null || true)"
if [ "${last_byte:-}" != $'\n' ]; then printf '\n' >> "$tmp"; fi
else
: > "$tmp"
fi
echo "README_CANDIDATE=$tmp" >> "$GITHUB_ENV"

- name: README drift on PR (report only)
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && matrix.rust == needs.msrv.outputs.version
shell: bash
run: |
set -euo pipefail
if [ ! -f README.md ]; then
echo "README.md missing in repo; nothing to compare."
exit 0
fi
if ! diff -u --label "README.md (HEAD)" --label "README.md (generated)" README.md "$README_CANDIDATE" >/tmp/readme.diff 2>/dev/null; then
echo "::warning::README differs on PR. Tests proceed with HEAD, not rewriting the file."
git --no-pager diff --no-index -- README.md "$README_CANDIDATE" || true
else
echo "README is up to date (PR)."
fi

- name: README autocommit on main (push or PR)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.rust == needs.msrv.outputs.version
id: readme_update
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
need_update=0
if [ ! -f README.md ]; then
need_update=1
elif ! cmp -s README.md "$README_CANDIDATE"; then
need_update=1
fi
if [ "$need_update" -eq 0 ]; then
echo "README is up to date (main)."
echo "updated=false" >> "$GITHUB_OUTPUT"
exit 0
fi

echo "Updating README working tree..."
cp -f "$README_CANDIDATE" README.md
git add README.md

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global --add safe.directory "$GITHUB_WORKSPACE"

git commit -m "chore(readme): auto-refresh [skip ci]" || true

set +e
git pull --rebase --autostash origin main
git push origin HEAD:main
rc=$?
set -e

if [ $rc -eq 0 ]; then
echo "Push to main succeeded."
echo "updated=true" >> "$GITHUB_OUTPUT"
# вычистим дерево, чтобы дальше всё было чисто
git fetch origin main
git reset --hard origin/main
git clean -fdx
exit 0
fi

echo "Push to main denied (likely branch protection). Preparing PR..."
echo "updated=true" >> "$GITHUB_OUTPUT"
# создаём ветку для PR
BR="ci/readme-auto-refresh"
git switch -c "$BR" || git checkout -b "$BR"
# коммит уже есть, просто запушим ветку
git push -u origin "$BR"

- name: Create pull request for README (fallback)
if: steps.readme_update.outputs.updated == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main'
if: steps.readme_update.outputs.updated == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.rust == needs.msrv.outputs.version
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: ci/readme-auto-refresh
title: "chore(readme): auto-refresh"
body: |
Автоматически сгенерированный апдейт README. Прямой пуш в `main` запрещён политикой, поэтому открыт PR.
commit-message: "chore(readme): auto-refresh [skip ci]"
labels: ci, chore
delete-branch: true
# ---------- end README handling ----------

- name: Check formatting (nightly rustfmt)
if: matrix.rust == needs.msrv.outputs.version
uses: ./.github/actions/cargo-fmt
with:
toolchain: nightly

- name: Clippy (MSRV)
- name: Clippy (${{ matrix.rust }})
uses: ./.github/actions/cargo-clippy
with:
toolchain: ${{ steps.msrv.outputs.msrv }}
toolchain: ${{ matrix.rust }}
all-features: ${{ inputs.all-features }}

- name: Cargo deny
if: matrix.rust == needs.msrv.outputs.version
uses: ./.github/actions/cargo-deny

- name: Tests (MSRV)
- name: Tests (${{ matrix.rust }})
uses: ./.github/actions/cargo-test
with:
toolchain: ${{ steps.msrv.outputs.msrv }}
toolchain: ${{ matrix.rust }}
all-features: ${{ inputs.all-features }}

- name: Security audit
if: matrix.rust == needs.msrv.outputs.version
uses: ./.github/actions/cargo-audit

- name: Auto-commit README changes (any branch)
if: always()
if: always() && matrix.rust == needs.msrv.outputs.version
run: |
set -euo pipefail
if ! cmp -s README.md "$README_CANDIDATE"; then
echo "README changed, committing..."
cp -f "$README_CANDIDATE" README.md
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git add README.md
git commit -m "chore(readme): auto-refresh [skip ci]" || true
else
echo "README already up to date."
fi

- name: Ensure tree is clean before package
if: matrix.rust == needs.msrv.outputs.version
shell: bash
run: |
set -euo pipefail
if ! git diff --quiet; then
echo "Working tree is dirty:"
git status --porcelain
exit 1
fi

- name: Package (dry-run)
run: cargo +${{ steps.msrv.outputs.msrv }} package --locked
if: matrix.rust == needs.msrv.outputs.version
run: cargo +${{ matrix.rust }} package --locked

coverage:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 2 months ago

  • General fix: Explicitly set a permissions: block so that the workflow (or individual jobs) only receive the minimal required set of permissions for their steps to work.
  • Best single fix for this snippet:
    Add a top-level permissions: block (applies to all jobs) with the most restrictive feasible permissions, e.g. permissions: { contents: read }, and extend specific jobs that need more (here likely the ci job, specifically where it commits, pushes, or makes a pull request) with a permissions: block giving contents: write and pull-requests: write.
  • Specific changes:
    • Add at the top level of the workflow (permissions: after name: and before on:) with contents: read.
    • In the ci job, add a permissions: block setting:
      permissions:
        contents: write
        pull-requests: write
      before runs-on: ubuntu-latest.
  • What is needed:
    • Insert top-level permissions: contents: read
    • Add job-level explicit permissions for ci job as above.

Suggested changeset 1
.github/workflows/reusable-ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml
--- a/.github/workflows/reusable-ci.yml
+++ b/.github/workflows/reusable-ci.yml
@@ -3,6 +3,8 @@
 # SPDX-License-Identifier: MIT
 
 name: Reusable CI
+permissions:
+  contents: read
 
 on:
   workflow_call:
@@ -37,6 +39,9 @@
           echo "Using MSRV: $RV"
 
   ci:
+    permissions:
+      contents: write
+      pull-requests: write
     runs-on: ubuntu-latest
     needs: msrv
     strategy:
EOF
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: MIT

name: Reusable CI
permissions:
contents: read

on:
workflow_call:
@@ -37,6 +39,9 @@
echo "Using MSRV: $RV"

ci:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
needs: msrv
strategy:
Copilot is powered by AI and may make mistakes. Always verify output.
runs-on: ubuntu-latest
needs: ci
steps:
- uses: actions/checkout@v5

- name: Install Rust (stable)
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
components: llvm-tools-preview

- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov

- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}

- name: Generate coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info

- name: Upload to Codecov
uses: codecov/codecov-action@v4
with:
files: ./lcov.info
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
Comment on lines +248 to +277

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 2 months ago

To resolve this issue, add a permissions: block specifying the minimum required permissions, thereby adhering to the principle of least privilege. The best practice is to place this block at the top level of the workflow YAML file, so that all jobs inherit least-privilege permissions unless overridden at the job level.

  • Add a top-level permissions: block after the name: declaration, before on:.
  • For most CI/CD workflows that do not require write access to repository contents, the recommended setting is contents: read.
  • If jobs require other permissions (e.g., pull-requests: write or contents: write for auto-committing changes), consider setting those at the individual job level. However, as a minimal fix and starting point, set contents: read at the top level.

What to change:

  • Add after line 5: name: Reusable CI a block:
    permissions:
      contents: read
    
  • If later, any job (e.g., the "auto-commit README changes" step) is shown to need contents: write, you may override the permissions for that specific job. For now, the minimal recommended fix is to set the top-level block to restrict all jobs to read-only.

Suggested changeset 1
.github/workflows/reusable-ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml
--- a/.github/workflows/reusable-ci.yml
+++ b/.github/workflows/reusable-ci.yml
@@ -3,6 +3,8 @@
 # SPDX-License-Identifier: MIT
 
 name: Reusable CI
+permissions:
+  contents: read
 
 on:
   workflow_call:
EOF
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: MIT

name: Reusable CI
permissions:
contents: read

on:
workflow_call:
Copilot is powered by AI and may make mistakes. Always verify output.

2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading