Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/4982 unwanted dependency checks #5509

61 changes: 61 additions & 0 deletions .github/workflows/unwanted-dependencies-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: unwanted-dependencies-check

Copy link
Member

@charles-chenzz charles-chenzz Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this yaml run on prow job? as we have an issue which want to migrate some actions workflows to prow
#5486

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a requirement to make the job optional, is it possible in prow to always run it but not cancelling the other pipeline?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure, but prow can run multiple jobs and choose to make some jobs optional. we might need to investigate prow( but prow is quite a huge project)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have confirmed with @natasha41575, it is decided that for this PR we'll still stick to GH action, and if your PR for prow is already merged we can migrate it to prow

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my PR for prow is still in pending review, we should merge this first

# Trigger the workflow on pull requests and direct pushes to any branch
on:
push:
pull_request:

permissions:
contents: read

jobs:
conditional-changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
doc: ${{ steps.filter.outputs.doc }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
doc:
- 'site/**'

unwanted-dependency-check:
name: Run unwanted dependencies check
runs-on: ubuntu-latest
needs: conditional-changes
# Ignore docs changes, and only apply for pull requests from different repository
if: (needs.conditional-changes.outputs.doc == 'false' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: Clone the code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.work
- name: 'Setup jq'
uses: dcarbone/install-jq-action@v2
with:
version: '1.7'
force: 'true'
- name: Run script
run: |
sudo apt-get update
sudo apt-get install wget -y
./hack/unwanted-dependencies-check.sh
- name: Report failure
uses: nashmaniac/[email protected]
# Only report failures of pushes (PRs have are visible through the Checks section) to the default branch
if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/master'
with:
title: 📦 unwanted-dependencies-check failed for ${{ github.sha }}
token: ${{ secrets.GITHUB_TOKEN }}
labels: kind/dependencies
body: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ site/.hugo_build.lock

# goreleaser artifacts
**/dist/

# unwanted dependency list json
hack/unwanted-dependencies.json
133 changes: 133 additions & 0 deletions hack/unwanted-dependencies-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env bash
# Copyright 2024 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0

set -o pipefail
set -o nounset
set +u

declare -i rc=0
declare -a POSTIONAL_ARGS=()

# Whitelisted dependencies
declare -a WHITELIST=()

declare -x GO11MODULES=yes
declare -x GOFLAGS=-mod=mod

# Explicit path of the unwanted dependency list
JSON_PATH_URL=""
JSON_PATH_LOCAL=""
READ_PATH=""

parse_args() {
while [[ "$1" != "" ]]; do
case $1 in
-u | --url )
shift
# Use json outside of repository
JSON_PATH_URL="${1}"
;;
-f | --file )
shift
# Use local json file
JSON_PATH_LOCAL="${1}"
;;
*)
;;
esac
shift
done
}

check_requirements() {

if [[ -z $(which jq) ]]; then
rc=1
echo "Error: jq not found, please install jq."
exit ${rc}
fi

if [[ -z $(which wget) ]]; then
rc=1
echo "Error: wget not found, please install wget."
exit ${rc}
fi
}

pull_unwanted_dependencies_json() {

if [[ ! -z "${JSON_PATH_URL}" ]]; then
echo "${JSON_PATH_URL}"
# Expected to be executed from root
wget "${JSON_PATH_URL}" -O ${PWD}/hack/unwanted-dependencies.json
READ_PATH=${PWD}/hack/unwanted-dependencies.json
elif [[ ! -z "${JSON_PATH_LOCAL}" ]]; then
echo "${JSON_PATH_LOCAL}"
# Expected to be executed from root
JSON_PATH_LOCAL=$(realpath "${JSON_PATH_LOCAL}")
if [[ -z $(stat ) ]]; then
rc=1
echo "Error: block list not supplied, please define block list file path."
exit ${rc}
fi
READ_PATH=$(realpath ${JSON_PATH_LOCAL})
else
# Default behavior: pull unwanted-dependencies.json from kubernetes/kubernetes upstream repo
JSON_PATH_URL='https://raw.githubusercontent.com/kubernetes/kubernetes/master/hack/unwanted-dependencies.json'
wget "${JSON_PATH_URL}" -O "${PWD}/hack/unwanted-dependencies.json"
READ_PATH="${PWD}/hack/unwanted-dependencies.json"
fi
}

compile_whitelist() {
for dep in $(jq -r '.status.unwantedReferences | keys[]' "${READ_PATH}"); do
echo "checking $dep for whitelist..."
for downstream in $(jq -r '.status.unwantedReferences["'"$dep"'"]' "${READ_PATH}"); do
if [[ $downstream == *"kustomize"* ]]; then
if [[ "${WHITELIST[*]}" =~ "${dep}" ]]; then
continue
else
WHITELIST+=("$dep")
break
fi
fi
done
done
}

check_unwanted_dependencies(){
for dep in $(jq -r '.spec.unwantedModules | keys[]' "${READ_PATH}"); do
for file in $(find . \( -type f -and -path '*/kyaml/*' -or -path '*/api/*' -or -path '*/kustomize/*' \)| fgrep go.sum); do
if [[ $(cat $file | fgrep $dep) && ! ${WHITELIST[@]} =~ "$dep" ]]; then
rc=1
echo "Error: unwanted dependencies found. ($dep at $(realpath $file))"
fi
done
done

for upstream in $(jq -r '.status.unwantedReferences | keys[]' "${READ_PATH}"); do
for ref in $(jq -r '.status.unwantedReferences.'\"${upstream}\"'[]' "${READ_PATH}"); do
if [[ $(go mod graph | fgrep $upstream | fgrep $ref) && ! ${WHITELIST[@]} =~ "$upstream" ]]; then
rc=1
echo "Error: unwanted references found on one of the dependencies. ($upstream depends on $ref))"
fi
done
done

if [[ $rc == 0 ]]; then
echo "No unwanted dependency detected."
fi

exit $rc
}

main() {
parse_args $@
check_requirements
pull_unwanted_dependencies_json
compile_whitelist
check_unwanted_dependencies
}

main $@
Loading