diff --git a/.github/scripts/get-changeset-arguments.sh b/.github/scripts/get-changeset-arguments.sh new file mode 100755 index 0000000000..31e0c5fee2 --- /dev/null +++ b/.github/scripts/get-changeset-arguments.sh @@ -0,0 +1,93 @@ +#!/bin/bash -e + +# This script takes a list of adapter names to release and outputs the +# arguments to pass to `yarn changeset version`. +# +# By default `yarn changeset version` will consume all changesets. If we want +# to release a subset of packages, the only way to tell changeset is to tell +# it which packages to ignore. +# It is not allowed to ignore a package in a changeset without ignoring all +# the packages in that changeset. And it is not allowed to ignore a package +# that is a dependency of a package that is not ignored. + +if [[ -z "$*" ]]; then + # Output to stderr and exit 0 because an empty list is valid input and + # should result in zero changeset arguments. + # So there should be no output to stdout and success exit code. + echo "Usage: $0 " >&2 + exit 0 +fi + +add_package_deps() { + packages="$1" + { + for package in $packages; do + echo $package + package_file="$(git grep -l '"name": "'"$package"'"' packages)" + jq -r '.dependencies | keys[]' "$package_file" | grep '@chainlink/' | grep -v '@chainlink/external-adapter-framework' + done + } | sort -u +} + +get_packages_from_changeset_files() { + git grep -Eh "^'@chainlink/[^']*-adapter': (major|minor|patch)$" "$@" | sed -E "s#^'(@chainlink/[^']*-adapter)': (major|minor|patch).*\$#\\1#" | sort -u +} + +add_changeset_deps() { + packages="$1" + { + for package in $packages; do + pattern="^'$package': (major|minor|patch)\$" + for changeset_file in $(git grep -lE "$pattern" .changeset); do + get_packages_from_changeset_files "$changeset_file" + done + done + } | sort -u +} + +add_deps() { + packages="$1" + packages=$(add_changeset_deps "$packages") + packages=$(add_package_deps "$packages") + echo "$packages" +} + +add_transitive_deps() { + packages="$1" + new_packages=$(add_deps "$packages") + while [[ "$new_packages" != "$packages" ]]; do + packages="$new_packages" + new_packages="$(add_deps "$packages")" + done + echo "$packages" +} + +get_adapter_packages() { + adapter_names="$(echo "$*" | sed -e 's/ *, */ /g')" + for adapter_name in $adapter_names; do + package_name="@chainlink/${adapter_name}-adapter" + if git grep -q '"name": "'"$package_name"'"' packages; then + echo "$package_name" + else + echo "'$adapter_name' is not an adapter name." >&2 + exit 1 + fi + done +} + +adapter_packages="$(get_adapter_packages $*)" + +packages_to_include="$(add_transitive_deps "$adapter_packages")" + +if [[ -z "$packages_to_include" ]]; then + echo "'$*' does not result in anything to release." >&2 + exit 1 +fi + +echo "Not ignoring the following transitive dependencies:" >&2 +echo "$packages_to_include" >&2 + +all_packages=$(yarn workspaces list --json | jq -r '.name' | grep -v '@chainlink/external-adapters-js') +packages_to_ignore=$(echo "$all_packages" | grep -vFf <(echo "$packages_to_include")) + +echo "$packages_to_ignore" | sed -E 's|^.+|--ignore &|' | tr '\n' ' ' diff --git a/.github/scripts/run-changesets.sh b/.github/scripts/run-changesets.sh index 0b1503a32d..d12f676a76 100755 --- a/.github/scripts/run-changesets.sh +++ b/.github/scripts/run-changesets.sh @@ -1,7 +1,7 @@ #!/bin/bash -e # Run changesets -yarn changeset version +yarn changeset version "$@" # Recover all our changes (readmes, version bump) -git stash pop \ No newline at end of file +git stash pop diff --git a/.github/workflows/upsert-release-pr.yml b/.github/workflows/upsert-release-pr.yml index 485c998664..b577123625 100644 --- a/.github/workflows/upsert-release-pr.yml +++ b/.github/workflows/upsert-release-pr.yml @@ -2,6 +2,11 @@ name: Upsert Release PR on: workflow_dispatch: + inputs: + selected-adapters: + description: comma (and/or space) separated list of adapters to release (if empty, all changed adapters with changesets will be released) + required: false + default: '' # TODO: This entire workflow would be immensely sped up (~3min -> 30s tops) if: # - Readme generation did not require built TS files @@ -15,6 +20,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} UPSTREAM_BRANCH: 'HEAD~1' + SELECTED_ADAPTERS: ${{ inputs.selected-adapters }} steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -31,9 +37,14 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" + - name: Get changeset arguments + run: | + set -e + CHANGESET_ARGUMENTS="$(.github/scripts/get-changeset-arguments.sh "$SELECTED_ADAPTERS")" + echo "CHANGESET_ARGUMENTS=$CHANGESET_ARGUMENTS" >> "$GITHUB_ENV" - name: Temporarily calculate changesets to generate readmes with proper versions run: | - yarn changeset version + yarn changeset version $CHANGESET_ARGUMENTS git add -A git commit -m "Changesets mock" - name: Set up and install dependencies @@ -72,7 +83,7 @@ jobs: # performs git resets and we want to keep those changes, so we stash and then pop them here. # All the previous steps would technically make more sense in the script itself, but we # keep them as separate ones so it's easier to see them from the github UI to debug. - version: ./.github/scripts/run-changesets.sh + version: ./.github/scripts/run-changesets.sh ${{ env.CHANGESET_ARGUMENTS }} title: 'Release ${{ env.BUMPED_VERSION }}' commit: 'Release ${{ env.BUMPED_VERSION }}' - name: Tag changesets commit