Create PR about orphaned features (#49827)
Co-authored-by: Robert Sese <734194+rsese@users.noreply.github.com>
This commit is contained in:
109
.github/workflows/orphaned-features-check.yml
vendored
Normal file
109
.github/workflows/orphaned-features-check.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
name: 'Orphaned features check'
|
||||
|
||||
# **What it does**: Finds any data/features that are no longer used in the repo.
|
||||
# **Why we have it**: To avoid orphans into the repo.
|
||||
# **Who does it impact**: Docs content.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '20 16 * * 1' # Run every Monday at 16:20 UTC / 8:20 PST
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/orphaned-features-check.yml
|
||||
# In case any of the dependencies affect the script
|
||||
- 'package*.json'
|
||||
- 'src/data-directory/scripts/find-orphaned-features/**'
|
||||
- .github/actions/clone-translations/action.yml
|
||||
- .github/actions/node-npm-setup/action.yml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
orphaned-features-check:
|
||||
if: ${{ github.repository == 'github/docs-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout English repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
# Using a PAT is necessary so that the new commit will trigger the
|
||||
# CI in the PR. (Events from GITHUB_TOKEN don't trigger new workflows.)
|
||||
token: ${{ secrets.DOCS_BOT_PAT_READPUBLICKEY }}
|
||||
|
||||
# It's important because translations are often a bit behind.
|
||||
# So if a translation is a bit behind, it might still be referencing
|
||||
# a feature even though none of the English content does.
|
||||
- name: Clone all translations
|
||||
uses: ./.github/actions/clone-translations
|
||||
with:
|
||||
token: ${{ secrets.DOCS_BOT_PAT_READPUBLICKEY }}
|
||||
|
||||
- uses: ./.github/actions/node-npm-setup
|
||||
|
||||
- name: Check for orphaned features
|
||||
env:
|
||||
# Needed for gh
|
||||
GITHUB_TOKEN: ${{ secrets.DOCS_BOT_PAT_READPUBLICKEY }}
|
||||
DRY_RUN: ${{ github.event_name == 'pull_request'}}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
npm run find-orphaned-features -- find --verbose --output /tmp/orphaned-features.json
|
||||
|
||||
if [ -f /tmp/orphaned-features.json ]; then
|
||||
echo "Orphaned features found:"
|
||||
cat /tmp/orphaned-features.json
|
||||
else
|
||||
echo "No orphaned features found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Why only 5?
|
||||
# Because, we're not in a hurry and anything larger than that would
|
||||
# make the PR too intimidatingly big to review.
|
||||
npm run find-orphaned-features -- delete --max 5 --verbose /tmp/orphaned-features.json
|
||||
|
||||
git status
|
||||
|
||||
# When run on a pull_request, we're just testing the tooling.
|
||||
# Exit before it actually pushes the possible changes.
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo "Dry-run mode when run in a pull request"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Replicated from the translation pipeline PR-maker Action
|
||||
git config --global user.name "docs-bot"
|
||||
git config --global user.email "77750099+docs-bot@users.noreply.github.com"
|
||||
|
||||
date=$(date '+%Y-%m-%d-%H-%M')
|
||||
branchname=orphaned-features-$date-$GITHUB_RUN_ID
|
||||
|
||||
git checkout -b $branchname
|
||||
git commit -a -m "Delete orphaned features $date"
|
||||
git push origin $branchname
|
||||
|
||||
body=$(cat <<-EOM
|
||||
Found with the 'npm run find-orphaned-features' script.
|
||||
The orphaned features workflow file .github/workflows/orphaned-features-check.yml
|
||||
runs every Monday at 16:20 UTC / 8:20 PST.
|
||||
The first responder should just spot-check some of the orphans
|
||||
to make sure they aren't referenced anywhere
|
||||
and then approve and merge the pull request.
|
||||
For more information, see [Doc: Orphaned Features](https://github.com/github/docs-engineering/blob/main/docs/orphaned-features.md).
|
||||
EOM
|
||||
)
|
||||
|
||||
gh pr create \
|
||||
--title "Delete orphaned features ($date)" \
|
||||
--body "$body" \
|
||||
--repo github/docs-internal \
|
||||
--label docs-content-fr
|
||||
|
||||
- uses: ./.github/actions/slack-alert
|
||||
if: ${{ failure() && github.event_name == 'schedule' }}
|
||||
with:
|
||||
slack_channel_id: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }}
|
||||
slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
|
||||
42
src/data-directory/scripts/find-orphaned-features/delete.ts
Normal file
42
src/data-directory/scripts/find-orphaned-features/delete.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import chalk from 'chalk'
|
||||
import languages from '@/languages/lib/languages.js'
|
||||
|
||||
type Options = {
|
||||
verbose?: boolean
|
||||
max: number
|
||||
}
|
||||
|
||||
export async function deleteOrphans(filePath: string, options: Options) {
|
||||
const orphans = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
||||
if (!Array.isArray(orphans)) {
|
||||
throw new Error(`Expected an array of orphans in ${filePath}`)
|
||||
}
|
||||
let count = 0
|
||||
if (options.verbose) {
|
||||
console.log(chalk.yellow(`${orphans.length} orphans found in ${filePath}`))
|
||||
if (orphans.length > options.max) {
|
||||
console.log(chalk.yellow(`Only deleting the first ${options.max} orphans`))
|
||||
}
|
||||
}
|
||||
let countDeletions = 0
|
||||
for (const orphan of orphans.slice(0, options.max)) {
|
||||
count++
|
||||
const absolutePath = path.join(languages.en.dir, orphan)
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
throw new Error(`File does not exist: ${absolutePath} (number ${count} in ${filePath})`)
|
||||
}
|
||||
if (options.verbose) {
|
||||
console.log(chalk.green(`Deleting ${absolutePath}`))
|
||||
}
|
||||
fs.unlinkSync(absolutePath)
|
||||
countDeletions++
|
||||
}
|
||||
if (countDeletions > 0) {
|
||||
console.log(chalk.green(`Deleted ${countDeletions} orphans`))
|
||||
} else {
|
||||
console.log(chalk.yellow(`Deleted no orphans`))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { program } from 'commander'
|
||||
import { find } from './find'
|
||||
import { deleteOrphans } from './delete'
|
||||
|
||||
program
|
||||
.name('find-orphaned-features')
|
||||
@@ -15,4 +16,12 @@ program
|
||||
.option('-v, --verbose', 'Verbose')
|
||||
.action(find)
|
||||
|
||||
program
|
||||
.command('delete')
|
||||
.description('Delete features based on found orphans')
|
||||
.option('-m, --max <number>', 'Maximum number of files to delete', (val) => parseInt(val), 10)
|
||||
.option('-v, --verbose', 'Verbose')
|
||||
.argument('<orphans-json-filepath>', 'path to the JSON file')
|
||||
.action(deleteOrphans)
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
Reference in New Issue
Block a user