mirror of
https://github.com/thousandeyes/thousandeyes-sdk-python.git
synced 2026-06-19 17:36:51 +00:00
Fix: Resolve workflow vulnerabilities (#154)
Some checks failed
Python CI / build (push) Has been cancelled
Some checks failed
Python CI / build (push) Has been cancelled
* resolve workflow vulnerabilities * Set persist-credentials: false on checkouts before untrusted steps. Prevents GITHUB_TOKEN from being written to git config before pip/pytest in CI and before build steps in release. add-tag checkout keeps default credentials for GitHub release creation. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
fa5af77369
commit
a2aaa9f059
48
.github/workflows/python.yaml
vendored
48
.github/workflows/python.yaml
vendored
@ -1,3 +1,7 @@
|
|||||||
|
# Runs on pushes to main and on pull requests whose head branch lives in this
|
||||||
|
# repository (contributors with push access). Fork PRs are skipped: GitHub still
|
||||||
|
# starts the workflow, but the job does not run, so untrusted code is not installed
|
||||||
|
# or executed via pip/pytest.
|
||||||
name: Python CI
|
name: Python CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@ -6,36 +10,66 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
|
||||||
|
- id: packages
|
||||||
|
name: Discover packages
|
||||||
|
run: |
|
||||||
|
shopt -s nullglob
|
||||||
|
packages=()
|
||||||
|
for dir in ./thousandeyes-sdk-*/; do
|
||||||
|
name="${dir#./}"
|
||||||
|
name="${name%/}"
|
||||||
|
if ! printf '%s' "$name" | grep -Eq '^thousandeyes-sdk-[a-z0-9-]+$'; then
|
||||||
|
echo "Invalid package directory name: ${name}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
packages+=("$name")
|
||||||
|
done
|
||||||
|
if [ "${#packages[@]}" -eq 0 ]; then
|
||||||
|
echo "No thousandeyes-sdk-* packages found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
FOLDERS_JSON=$(printf '%s\n' "${packages[@]}" | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||||
|
echo "packages=${FOLDERS_JSON}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Install core module
|
- name: Install core module
|
||||||
run: pip install -e thousandeyes-sdk-core
|
run: pip install -e thousandeyes-sdk-core
|
||||||
|
|
||||||
- name: Install and test modules
|
- name: Install and test modules
|
||||||
|
env:
|
||||||
|
PACKAGES_JSON: ${{ steps.packages.outputs.packages }}
|
||||||
run: |
|
run: |
|
||||||
pip install pytest
|
pip install pytest
|
||||||
pip install coverage
|
pip install coverage
|
||||||
|
|
||||||
# Initialize coverage data file
|
|
||||||
coverage erase
|
coverage erase
|
||||||
|
|
||||||
for module in $(find . -maxdepth 1 -type d -name "thousandeyes-sdk-*" | cut -c 3-); do
|
mapfile -t modules < <(jq -r '.[]' <<< "$PACKAGES_JSON")
|
||||||
pip install -e $module
|
for module in "${modules[@]}"; do
|
||||||
coverage run --source=$module -m pytest $module
|
pip install -e "./${module}"
|
||||||
# Move the .coverage file to a unique name
|
coverage run --source="./${module}" -m pytest "./${module}"
|
||||||
mv .coverage .coverage.$module
|
mv .coverage ".coverage.${module}"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Combine all .coverage files
|
|
||||||
coverage combine .coverage.*
|
coverage combine .coverage.*
|
||||||
coverage report
|
coverage report
|
||||||
coverage xml
|
coverage xml
|
||||||
|
|||||||
79
.github/workflows/release.yaml
vendored
79
.github/workflows/release.yaml
vendored
@ -1,3 +1,5 @@
|
|||||||
|
# Manual release only. GitHub allows workflow_dispatch only for users with write
|
||||||
|
# access to this repository. PyPI publish is further gated by the "release" environment.
|
||||||
name: Release
|
name: Release
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@ -7,26 +9,72 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
validate-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
release_version: ${{ steps.validate.outputs.release_version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
- id: validate
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ inputs.releaseVersion }}
|
||||||
|
run: |
|
||||||
|
if ! printf '%s' "$RELEASE_VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$'; then
|
||||||
|
echo "Invalid releaseVersion: must match X.Y.Z or X.Y.ZrcN (e.g. 2.26.0 or 2.0.0rc1)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if git rev-parse "refs/tags/${RELEASE_VERSION}" >/dev/null 2>&1; then
|
||||||
|
echo "Tag ${RELEASE_VERSION} already exists" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "release_version=${RELEASE_VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
set-package-matrix:
|
set-package-matrix:
|
||||||
|
needs: validate-release
|
||||||
# This action returns all sub-packages to be published.
|
# This action returns all sub-packages to be published.
|
||||||
# It thens exports the variable to `matrix`, so that the deployment job is run individually for each sub-package
|
# It then exports the variable to `matrix`, so that the deployment job is run individually for each sub-package
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
packages: ${{ steps.packages.outputs.packages }}
|
packages: ${{ steps.packages.outputs.packages }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- id: packages
|
- id: packages
|
||||||
run: |
|
run: |
|
||||||
FOLDERS_JSON=$(find . -maxdepth 1 -type d -name "thousandeyes-sdk-*" | cut -c 3- | jq -R -s -c 'split("\n")[:-1]')
|
shopt -s nullglob
|
||||||
echo "packages=$FOLDERS_JSON" >> "$GITHUB_OUTPUT"
|
packages=()
|
||||||
|
for dir in ./thousandeyes-sdk-*/; do
|
||||||
|
name="${dir#./}"
|
||||||
|
name="${name%/}"
|
||||||
|
if ! printf '%s' "$name" | grep -Eq '^thousandeyes-sdk-[a-z0-9-]+$'; then
|
||||||
|
echo "Invalid package directory name: ${name}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
packages+=("$name")
|
||||||
|
done
|
||||||
|
if [ "${#packages[@]}" -eq 0 ]; then
|
||||||
|
echo "No thousandeyes-sdk-* packages found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
FOLDERS_JSON=$(printf '%s\n' "${packages[@]}" | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||||
|
echo "packages=${FOLDERS_JSON}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
deployment:
|
deployment:
|
||||||
needs: set-package-matrix
|
needs: [validate-release, set-package-matrix]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
package-name: ${{ fromJSON(needs.set-package-matrix.outputs.packages) }}
|
package-name: ${{ fromJSON(needs.set-package-matrix.outputs.packages) }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
environment:
|
environment:
|
||||||
name: release
|
name: release
|
||||||
@ -35,31 +83,40 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
persist-credentials: false
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
cache: pip
|
cache: pip
|
||||||
cache-dependency-path: '**/pyproject.toml'
|
cache-dependency-path: '**/pyproject.toml'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: pip install setuptools wheel build
|
||||||
pip install setuptools wheel build
|
|
||||||
- name: Build
|
- name: Build
|
||||||
|
env:
|
||||||
|
RELEASE_VERSION: ${{ needs.validate-release.outputs.release_version }}
|
||||||
|
PACKAGE_NAME: ${{ matrix.package-name }}
|
||||||
run: |
|
run: |
|
||||||
echo ${{ inputs.releaseVersion }} >> ${{ matrix.package-name }}/.version
|
printf '%s\n' "$RELEASE_VERSION" >> "${PACKAGE_NAME}/.version"
|
||||||
cp LICENSE NOTICE ${{ matrix.package-name }}/
|
cp LICENSE NOTICE "${PACKAGE_NAME}/"
|
||||||
python -m build ${{ matrix.package-name }} --outdir dist/
|
python -m build "${PACKAGE_NAME}" --outdir dist/
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
skip-existing: true
|
skip-existing: true
|
||||||
|
|
||||||
add-tag:
|
add-tag:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: deployment
|
needs: [validate-release, deployment]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ inputs.releaseVersion }}
|
tag_name: ${{ needs.validate-release.outputs.release_version }}
|
||||||
prerelease: false
|
prerelease: false
|
||||||
draft: false
|
draft: false
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user