зеркало из https://github.com/github/docs.git
Update orphaned assets workflow for use with translation repos (#31869)
This commit is contained in:
Родитель
1be0cd7033
Коммит
9eb0c2c0e1
|
@ -17,13 +17,46 @@ jobs:
|
|||
if: ${{ github.repository == 'github/docs-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout English repo
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
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.DOCUBOT_REPO_PAT }}
|
||||
|
||||
# TODO: Can be removed after we no longer keep translations in-repo
|
||||
- name: Remove existing language translations
|
||||
run: |
|
||||
rm -rf translations
|
||||
|
||||
- name: Checkout the es-es repo
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
with:
|
||||
repository: github/docs-internal.es-es
|
||||
token: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
|
||||
path: translations/es-ES
|
||||
|
||||
- name: Checkout the pt-br repo
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
with:
|
||||
repository: github/docs-internal.pt-br
|
||||
token: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
|
||||
path: translations/pt-BR
|
||||
|
||||
- name: Checkout the zh-cn repo
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
with:
|
||||
repository: github/docs-internal.zh-cn
|
||||
token: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
|
||||
path: translations/zh-CN
|
||||
|
||||
- name: Checkout the ja-jp repo
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
with:
|
||||
repository: github/docs-internal.ja-jp
|
||||
token: ${{ secrets.DOCUBOT_READORG_REPO_WORKFLOW_SCOPES }}
|
||||
path: translations/ja-JP
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@17f8bd926464a1afa4c6a11669539e9c1ba77048
|
||||
with:
|
||||
|
@ -43,7 +76,7 @@ jobs:
|
|||
./script/find-orphaned-assets.js | xargs git rm
|
||||
|
||||
# If nothing to commit, exit now. It's fine. No orphans.
|
||||
git status | grep 'nothing to commit' && exit 0
|
||||
git status -- ':!translations*' | grep 'nothing to commit' && exit 0
|
||||
|
||||
# Replicated from the translation pipeline PR-maker Action
|
||||
git config --global user.name "docubot"
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
name: Remove unused assets
|
||||
|
||||
# **What it does**:
|
||||
# **Why we have it**:
|
||||
# **Who does it impact**:
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '20 15 * * 0' # run every Sunday at 20:15 UTC / 12:15 PST
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
FREEZE: ${{ secrets.FREEZE }}
|
||||
|
||||
jobs:
|
||||
remove_unused_assets:
|
||||
name: Remove unused assets
|
||||
if: github.repository == 'github/docs-internal'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: ${{ env.FREEZE == 'true' }}
|
||||
run: |
|
||||
echo 'The repo is currently frozen! Exiting this workflow.'
|
||||
exit 1 # prevents further steps from running
|
||||
- name: Checkout
|
||||
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@17f8bd926464a1afa4c6a11669539e9c1ba77048
|
||||
with:
|
||||
node-version: '16.17.0'
|
||||
cache: npm
|
||||
- name: npm ci
|
||||
run: npm ci
|
||||
- name: Run scripts
|
||||
run: |
|
||||
script/remove-unused-assets.js > results.md
|
||||
- name: Get script results to use in PR body
|
||||
id: results
|
||||
uses: juliangruber/read-file-action@e0a316da496006ffd19142f0fd594a1783f3b512
|
||||
with:
|
||||
path: ./results.md
|
||||
- name: Remove script results file
|
||||
run: rm ./results.md
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e
|
||||
env:
|
||||
# Disable pre-commit hooks; they don't play nicely here
|
||||
HUSKY: '0'
|
||||
with:
|
||||
# need to use a token with repo and workflow scopes for this step
|
||||
token: ${{ secrets.OCTOMERGER_PAT_WITH_REPO_AND_WORKFLOW_SCOPE }}
|
||||
commit-message: Action ran script/remove-unused-assets.js
|
||||
title: Remove unused assets
|
||||
body:
|
||||
"Hello! This PR removes some files that exist in the repo but are not used in content or data files:\n\n
|
||||
${{ steps.results.outputs.content }}
|
||||
\n\nIf you have any questions, please contact @github/docs-engineering."
|
||||
labels: unused assets
|
||||
project: Core docs work for the current week
|
||||
project-column: Should do
|
||||
branch: remove-unused-assets
|
||||
- if: ${{ failure() && env.FREEZE != 'true' }}
|
||||
name: Delete remote branch (if previous steps failed)
|
||||
uses: dawidd6/action-delete-branch@47743101a121ad657031e6704086271ca81b1911
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branches: remove-unused-assets
|
|
@ -141,13 +141,6 @@ Run this one-time script to convert `if <feature name>` Liquid tags to `ifversio
|
|||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
### [`content-migrations/remove-unused-assets.js`](content-migrations/remove-unused-assets.js)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
@ -327,13 +320,6 @@ This script creates new static webhook payload files for a new version.
|
|||
---
|
||||
|
||||
|
||||
### [`enterprise-server-releases/ghes-to-ghae-versioning.js`](enterprise-server-releases/ghes-to-ghae-versioning.js)
|
||||
|
||||
Run this script to add versions frontmatter and Liquid conditionals for GitHub AE, based on anything currently versioned for the specified release of Enterprise Server. This script should be run as part of the Enterprise Server release process.
|
||||
|
||||
---
|
||||
|
||||
|
||||
### [`enterprise-server-releases/release-banner.js`](enterprise-server-releases/release-banner.js)
|
||||
|
||||
This script creates or removes a release candidate banner for a specified version.
|
||||
|
@ -415,13 +401,6 @@ Pass this script any old dotcom path (e.g., `articles/foo` or `foo.md`) and it w
|
|||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
### [`helpers/find-unused-assets.js`](helpers/find-unused-assets.js)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
@ -583,13 +562,6 @@ it's because you haven't installed all the *optional* dependencies. To do that,
|
|||
---
|
||||
|
||||
|
||||
### [`migrate-colors-primer-18.js`](migrate-colors-primer-18.js)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
### [`move-category-to-product.js`](move-category-to-product.js)
|
||||
|
||||
Move the files from a category directory to a top-level product and add redirects.
|
||||
|
@ -599,14 +571,7 @@ Move the files from a category directory to a top-level product and add redirect
|
|||
|
||||
### [`move-content.js`](move-content.js)
|
||||
|
||||
Helps you move (a.k.a. rename) a file or a folder and does what's needed with frontmatter redrect_from and equivalent in translations.
|
||||
|
||||
---
|
||||
|
||||
|
||||
### [`move-reusables-to-markdown.js`](move-reusables-to-markdown.js)
|
||||
|
||||
This script moves reusables out of YAML files into individual Markdown files.
|
||||
Helps you move (a.k.a. rename) a file or a folder and does what's needed with frontmatter redirect_from.
|
||||
|
||||
---
|
||||
|
||||
|
@ -666,13 +631,6 @@ An automated test checks for discrepancies between filenames and [autogenerated
|
|||
---
|
||||
|
||||
|
||||
### [`remove-unused-assets.js`](remove-unused-assets.js)
|
||||
|
||||
Run this script to remove reusables and image files that exist in the repo but are not used in content files. It also displays a list of unused variables. Set the `--dry-run` to flag to print results without deleting any files. For images you don't want to delete, add them to `ignoreList` in `lib/find-unused-assets.js`
|
||||
|
||||
---
|
||||
|
||||
|
||||
### [`rendered-content-link-checker.js`](rendered-content-link-checker.js)
|
||||
|
||||
This script goes through all content and renders their HTML and from there can analyze for various flaws (e.g. broken links)
|
||||
|
|
|
@ -1,256 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
import { fileURLToPath } from 'url'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import walk from 'walk-sync'
|
||||
import { execSync } from 'child_process'
|
||||
import { loadPages } from '../../lib/page-data.js'
|
||||
import patterns from '../../lib/patterns.js'
|
||||
import { supported } from '../../lib/enterprise-server-releases.js'
|
||||
import semver from 'semver'
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const imagesPath = [
|
||||
'/assets/enterprise/3.0',
|
||||
'/assets/enterprise/github-ae',
|
||||
'/assets/enterprise/2.22',
|
||||
'/assets/enterprise/2.21',
|
||||
'/assets/enterprise/2.20',
|
||||
]
|
||||
|
||||
// these paths should remain in the repo even if they are not referenced directly
|
||||
const ignoreList = [
|
||||
'/assets/images/help/site-policy',
|
||||
'/assets/images/site',
|
||||
'/assets/images/octicons',
|
||||
'/assets/fonts',
|
||||
]
|
||||
|
||||
// search these dirs for images or data references
|
||||
// content files are handled separately
|
||||
const dirsToGrep = ['includes', 'layouts', 'javascripts', 'stylesheets', 'README.md', 'data']
|
||||
|
||||
async function main() {
|
||||
const pages = await getEnglishPages()
|
||||
|
||||
// step 1. find assets referenced in content by searching page markdown
|
||||
const markdownImageData = await getMarkdownImageData(pages)
|
||||
|
||||
// step 2. find assets referenced in non-content directories
|
||||
const assetsReferencedInNonContentDirs = await getAssetsReferencedInNonContentDirs()
|
||||
|
||||
// step 3. remove enterprise assets that are only used on dotcom pages
|
||||
await removeDotcomOnlyImagesFromEnterprise(markdownImageData)
|
||||
|
||||
// step 4. find all assets that exist in the /assets/enterprise directory
|
||||
// and remove assets that are referenced in any files
|
||||
for (const directory of imagesPath) {
|
||||
const allImagesInDir = await getAllAssetsInDirectory(directory)
|
||||
await removeEnterpriseImages(
|
||||
markdownImageData,
|
||||
allImagesInDir,
|
||||
assetsReferencedInNonContentDirs,
|
||||
directory
|
||||
)
|
||||
}
|
||||
|
||||
// step 5. find all assets that exist in the /assets/images directory
|
||||
// and remove assets that are referenced in any files
|
||||
const allDotcomImagesInDir = await getAllAssetsInDirectory('/assets/images')
|
||||
await removeUnusedDotcomImages(
|
||||
markdownImageData,
|
||||
allDotcomImagesInDir,
|
||||
assetsReferencedInNonContentDirs
|
||||
)
|
||||
}
|
||||
|
||||
// Returns an object of all the images referenced in Markdown
|
||||
// content and their associated versions
|
||||
//
|
||||
// key: image asset path
|
||||
// value: object key is the plan name and the object value is the plan's range
|
||||
//
|
||||
// ex: {
|
||||
// '/assets/images/foo/bar.png': { 'enterprise-server': '<=2.22'},
|
||||
// '/assets/images/bar/foo/png': { 'github-ae': '*'}
|
||||
// }
|
||||
async function getMarkdownImageData(pages) {
|
||||
const imageData = {}
|
||||
|
||||
// loop through each page and get all /assets/images references from Markdown
|
||||
for (const page of pages) {
|
||||
const fullContent = [page.intro, page.title, page.product, page.markdown].join()
|
||||
const pageImageList = await getImageReferencesOnPage(fullContent)
|
||||
|
||||
// for each asset reference on a Markdown page, see if it needs to be
|
||||
// added to the imageData object
|
||||
for (const imagePath of pageImageList) {
|
||||
// if the image isn't already there add the image and its versions
|
||||
if (!Object.prototype.hasOwnProperty.call(imageData, imagePath)) {
|
||||
imageData[imagePath] = page.versions
|
||||
continue
|
||||
}
|
||||
|
||||
// if the image reference already exists, see if any new version keys
|
||||
// or values need to be added or updated
|
||||
for (const pageVersion in page.versions) {
|
||||
const imageVersions = imageData[imagePath]
|
||||
const versionAlreadyExists = Object.prototype.hasOwnProperty.call(
|
||||
imageVersions,
|
||||
pageVersion
|
||||
)
|
||||
const existingVersionRangeIsAll = imageVersions[pageVersion] === '*'
|
||||
|
||||
if (!versionAlreadyExists) {
|
||||
imageVersions[pageVersion] = page.versions[pageVersion]
|
||||
} else {
|
||||
// github-ae and free-pro-team versions only have '*' range
|
||||
if (pageVersion !== 'enterprise-server' || existingVersionRangeIsAll) continue
|
||||
|
||||
const rangeOfVersionToAddIsAll = page.versions[pageVersion] === '*'
|
||||
// see if the version to add is a superset of the existing version
|
||||
const existingVersionIsSubsetOfNewVersion = semver.lt(
|
||||
semver.coerce(page.versions[pageVersion].replace('*', 0)),
|
||||
semver.coerce(imageVersions[pageVersion].replace('*', 0))
|
||||
)
|
||||
|
||||
// only update the version range if the existing range is not '*' or
|
||||
// the new range is a superset of the existing range
|
||||
if (rangeOfVersionToAddIsAll || existingVersionIsSubsetOfNewVersion) {
|
||||
imageVersions[pageVersion] = page.versions[pageVersion]
|
||||
}
|
||||
}
|
||||
imageData[imagePath] = imageVersions
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageData
|
||||
}
|
||||
|
||||
async function getEnglishPages() {
|
||||
const pages = await loadPages()
|
||||
return pages.filter((page) => page.languageCode === 'en')
|
||||
}
|
||||
|
||||
async function getAllAssetsInDirectory(directory) {
|
||||
return walk(path.join(process.cwd(), directory), { directories: false }).map((relPath) =>
|
||||
path.join(directory, relPath)
|
||||
)
|
||||
}
|
||||
|
||||
async function getAssetsReferencedInNonContentDirs() {
|
||||
const regex = patterns.imagePath.source
|
||||
const grepCmd = `egrep -rh '${regex}' ${dirsToGrep.join(' ')}`
|
||||
const grepResults = execSync(grepCmd).toString()
|
||||
return await getImageReferencesOnPage(grepResults)
|
||||
}
|
||||
|
||||
async function getImageReferencesOnPage(text) {
|
||||
return (text.match(patterns.imagePath) || []).map((ref) => {
|
||||
return ref.replace(/\.\.\//g, '').trim()
|
||||
})
|
||||
}
|
||||
|
||||
// loop through images referenced in Markdown and check whether the image
|
||||
// is only referenced in pages versioned for free-pro-team. If the image
|
||||
// is only used on free-pro-team pages, then it shouldn't exist in the
|
||||
// assets/enterprise directory.
|
||||
function removeDotcomOnlyImagesFromEnterprise(markdownImageData) {
|
||||
for (const image in markdownImageData) {
|
||||
const imageVersions = markdownImageData[image]
|
||||
if (!Object.prototype.hasOwnProperty.call(imageVersions, 'enterprise-server')) {
|
||||
supported.forEach((enterpriseReleaseNumber) => {
|
||||
const imagePath = path.join(
|
||||
__dirname,
|
||||
'../..',
|
||||
`/assets/enterprise/${enterpriseReleaseNumber}`,
|
||||
image
|
||||
)
|
||||
if (fs.existsSync(imagePath)) fs.unlinkSync(imagePath)
|
||||
})
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(imageVersions, 'github-ae')) {
|
||||
const imagePath = path.join(__dirname, '../..', '/assets/enterprise/github-ae', image)
|
||||
if (fs.existsSync(imagePath)) fs.unlinkSync(imagePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop through each image in a directory under /assets/enterprise
|
||||
// and check the image's version to determine if the image should be
|
||||
// removed from the directory
|
||||
async function removeEnterpriseImages(
|
||||
markdownImageData,
|
||||
directoryImageList,
|
||||
assetsReferencedInNonContentDirs,
|
||||
directory
|
||||
) {
|
||||
const directoryVersion = directory.split('/').pop()
|
||||
for (const directoryImage of directoryImageList) {
|
||||
// get the asset's format that is stored in the markdownImageData object
|
||||
const imageDataKey = directoryImage.replace(directory, '')
|
||||
|
||||
// if the image is in a non content file (i.e., javascript or data file)
|
||||
// we don't have the page version info so assume it's used in all versions
|
||||
if (assetsReferencedInNonContentDirs.includes(imageDataKey)) continue
|
||||
|
||||
const imageFullPath = path.join(process.cwd(), directoryImage)
|
||||
const imageVersions = markdownImageData[imageDataKey]
|
||||
|
||||
// if the asset isn't referenced in Markdown at all, remove it
|
||||
if (markdownImageData[imageDataKey] === undefined) {
|
||||
fs.unlinkSync(imageFullPath)
|
||||
continue
|
||||
}
|
||||
|
||||
// if the asset is in Markdown but is not used on GitHub AE pages, remove it
|
||||
if (
|
||||
directoryVersion === 'github-ae' &&
|
||||
!Object.prototype.hasOwnProperty.call(imageVersions, 'github-ae')
|
||||
) {
|
||||
fs.unlinkSync(imageFullPath)
|
||||
continue
|
||||
// if the asset is in Markdown but is not used on a page versioned for the
|
||||
// directoryVersion (i.e., GHES release number), remove it
|
||||
}
|
||||
if (
|
||||
directoryVersion !== 'github-ae' &&
|
||||
!Object.prototype.hasOwnProperty.call(imageVersions, 'enterprise-server')
|
||||
) {
|
||||
fs.unlinkSync(imageFullPath)
|
||||
continue
|
||||
}
|
||||
if (
|
||||
directoryVersion !== 'github-ae' &&
|
||||
semver.lt(
|
||||
semver.coerce(directoryVersion),
|
||||
semver.coerce(imageVersions['enterprise-server'].replace('*', 0.0))
|
||||
)
|
||||
) {
|
||||
fs.unlinkSync(imageFullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop through each file in /assets/images and check if
|
||||
async function removeUnusedDotcomImages(
|
||||
markdownImageData,
|
||||
directoryImageList,
|
||||
assetsReferencedInNonContentDirs
|
||||
) {
|
||||
for (const directoryImage of directoryImageList) {
|
||||
if (ignoreList.find((ignored) => directoryImage.startsWith(ignored))) continue
|
||||
|
||||
// if the image is in a non content file (i.e., javascript or data file)
|
||||
// we don't have the page version info so assume it's used in all versions
|
||||
if (assetsReferencedInNonContentDirs.includes(directoryImage)) continue
|
||||
|
||||
if (markdownImageData[directoryImage] === undefined) {
|
||||
fs.unlinkSync(path.join(process.cwd(), directoryImage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.catch(console.error)
|
||||
.finally(() => console.log('Done!'))
|
|
@ -1,123 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
import { flatten } from 'lodash-es'
|
||||
import path from 'path'
|
||||
import walk from 'walk-sync'
|
||||
import { execSync } from 'child_process'
|
||||
import assert from 'assert'
|
||||
import loadSiteData from '../../lib/site-data.js'
|
||||
import { loadPages } from '../../lib/page-data.js'
|
||||
import patterns from '../../lib/patterns.js'
|
||||
import getDataReferences from '../../lib/get-liquid-data-references.js'
|
||||
|
||||
const imagesPath = '/assets/images'
|
||||
|
||||
// these paths should remain in the repo even if they are not referenced directly
|
||||
const ignoreList = ['/assets/images/help/site-policy', 'site.data.reusables.policies']
|
||||
|
||||
// search these dirs for images or data references
|
||||
// content files are handled separately in assetsReferencedInContent
|
||||
const dirsToGrep = [
|
||||
'includes',
|
||||
'layouts',
|
||||
'javascripts',
|
||||
'stylesheets',
|
||||
'README.md',
|
||||
'data/reusables',
|
||||
'data/variables',
|
||||
]
|
||||
|
||||
const validArgs = ['reusables', 'variables', 'images']
|
||||
|
||||
export default async function findUnusedAssets(assetType) {
|
||||
assert(validArgs.includes(assetType), `arg must be one of: ${validArgs.join(', ')}`)
|
||||
|
||||
const pages = await getEnglishPages()
|
||||
const data = await loadSiteData()
|
||||
|
||||
// step 1. find all assets that exist in the repo
|
||||
const allReusablesInRepo = data.en.site.data.reusables
|
||||
const allVariablesInRepo = data.en.site.data.variables
|
||||
const allImagesInRepo = getAllImagesInRepo()
|
||||
|
||||
// step 2. find assets referenced in content by searching page markdown
|
||||
const assetsReferencedInContent = flatten(
|
||||
pages.map((page) => {
|
||||
const fullContent = [page.intro, page.title, page.product, page.markdown].join()
|
||||
|
||||
return assetType === 'images'
|
||||
? getImageReferences(fullContent)
|
||||
: getDataReferences(fullContent)
|
||||
})
|
||||
)
|
||||
|
||||
// step 3. find assets referenced in non-content directories
|
||||
const assetsReferencedInNonContentDirs = getAssetsReferencedInNonContentDirs(assetType)
|
||||
|
||||
// step 4. combine all the referenced assets into one array
|
||||
const allReferencedAssets = [
|
||||
...new Set(assetsReferencedInContent.concat(assetsReferencedInNonContentDirs)),
|
||||
]
|
||||
|
||||
// step 5. return asssets that exist but are not referenced
|
||||
switch (assetType) {
|
||||
case 'images':
|
||||
return getUnusedImages(allImagesInRepo, allReferencedAssets)
|
||||
case 'reusables':
|
||||
return getUnusedData(allReusablesInRepo, assetType, allReferencedAssets)
|
||||
case 'variables':
|
||||
return getUnusedData(allVariablesInRepo, assetType, allReferencedAssets)
|
||||
}
|
||||
}
|
||||
|
||||
async function getEnglishPages() {
|
||||
const pages = await loadPages()
|
||||
return pages.filter((page) => page.languageCode === 'en')
|
||||
}
|
||||
|
||||
function getAllImagesInRepo() {
|
||||
return walk(path.join(process.cwd(), imagesPath), { directories: false })
|
||||
.filter((relPath) => !relPath.endsWith('.md') && !relPath.match(/^(octicons|site)\//))
|
||||
.map((relPath) => path.join(imagesPath, relPath))
|
||||
}
|
||||
|
||||
function getAssetsReferencedInNonContentDirs(assetType) {
|
||||
const regex = assetType === 'images' ? patterns.imagePath.source : patterns.dataReference.source
|
||||
|
||||
const grepCmd = `egrep -rh '${regex}' ${dirsToGrep.join(' ')}`
|
||||
|
||||
const grepResults = execSync(grepCmd).toString()
|
||||
|
||||
return assetType === 'images' ? getImageReferences(grepResults) : getDataReferences(grepResults)
|
||||
}
|
||||
|
||||
function getImageReferences(text) {
|
||||
return (text.match(patterns.imagePath) || []).map((ref) => {
|
||||
return ref.replace(/\.\.\//g, '').trim()
|
||||
})
|
||||
}
|
||||
|
||||
function getUnusedData(allDataInRepo, assetType, allReferencedAssets) {
|
||||
const unusedData = []
|
||||
|
||||
Object.keys(allDataInRepo).forEach((filename) => {
|
||||
Object.keys(allDataInRepo[filename]).forEach((key) => {
|
||||
const name = `site.data.${assetType}.${filename}.${key}`
|
||||
if (
|
||||
!allReferencedAssets.includes(name) &&
|
||||
!ignoreList.find((ignored) => name.startsWith(ignored))
|
||||
) {
|
||||
unusedData.push(name)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return unusedData
|
||||
}
|
||||
|
||||
function getUnusedImages(allImagesInRepo, allReferencedAssets) {
|
||||
return allImagesInRepo.filter(
|
||||
(image) =>
|
||||
!allReferencedAssets.includes(image) &&
|
||||
!ignoreList.find((ignored) => image.startsWith(ignored))
|
||||
)
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// [start-readme]
|
||||
//
|
||||
// Run this script to remove reusables and image files that exist in the repo but
|
||||
// are not used in content files. It also displays a list of unused variables. Set
|
||||
// the `--dry-run` to flag to print results without deleting any files. For images
|
||||
// you don't want to delete, add them to `ignoreList` in `lib/find-unused-assets.js`
|
||||
//
|
||||
// [end-readme]
|
||||
|
||||
import { fileURLToPath } from 'url'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import findUnusedAssets from './helpers/find-unused-assets.js'
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const dryRun = process.argv.slice(2).includes('--dry-run')
|
||||
main()
|
||||
|
||||
async function main() {
|
||||
if (dryRun) {
|
||||
console.log('This is a dry run! The script will report unused files without deleting anything.')
|
||||
}
|
||||
|
||||
removeUnusedReusables(await findUnusedAssets('reusables'))
|
||||
removeUnusedImages(await findUnusedAssets('images'))
|
||||
printUnusedVariables(await findUnusedAssets('variables'))
|
||||
}
|
||||
|
||||
function removeUnusedReusables(reusables) {
|
||||
logMessage(reusables, 'reusable')
|
||||
|
||||
reusables.forEach((reusable) => {
|
||||
const reusablePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
reusable.replace('site', '').replace(/\./g, '/').replace(/$/, '.md')
|
||||
)
|
||||
dryRun ? console.log(reusable) : fs.unlinkSync(reusablePath)
|
||||
})
|
||||
}
|
||||
|
||||
function removeUnusedImages(images) {
|
||||
logMessage(images, 'image')
|
||||
|
||||
images.forEach((image) => {
|
||||
const imagePath = path.join(__dirname, '..', image)
|
||||
dryRun ? console.log(image) : fs.unlinkSync(imagePath)
|
||||
})
|
||||
}
|
||||
|
||||
// multiple variables are embedded in within the same YML file
|
||||
// so we can't just delete the files, and we can't parse/modify
|
||||
// them either because js-yaml does not preserve whitespace :[
|
||||
function printUnusedVariables(variables) {
|
||||
logMessage(variables, 'variable')
|
||||
|
||||
variables.forEach((variable) => {
|
||||
const variableKey = variable.split('.').pop()
|
||||
const variablePath = path.join(
|
||||
process.cwd(),
|
||||
variable
|
||||
.replace('site', '')
|
||||
.replace(`.${variableKey}`, '')
|
||||
.replace(/\./g, '/')
|
||||
.replace(/$/, '.yml')
|
||||
)
|
||||
dryRun
|
||||
? console.log(variable)
|
||||
: console.log(
|
||||
`* found but did not delete '${variableKey}' in ${variablePath.replace(
|
||||
process.cwd(),
|
||||
''
|
||||
)}`
|
||||
)
|
||||
})
|
||||
|
||||
if (!dryRun) console.log('\nYou will need to manually delete any variables you want to remove.')
|
||||
}
|
||||
|
||||
function logMessage(list, type) {
|
||||
let action
|
||||
|
||||
if (dryRun) {
|
||||
action = '\n**Found'
|
||||
} else {
|
||||
action = type === 'variable' ? ':eyes: **Found' : ':scissors: **Removed'
|
||||
}
|
||||
|
||||
console.log(`${action} ${list.length} unused ${type} ${list.length === 1 ? 'file' : 'files'}**\n`)
|
||||
}
|
Загрузка…
Ссылка в новой задаче