Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions lib/gh/workers/enable-lint-feature.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { flow } from 'lodash-es'
import * as pull from './pull.mjs'
import { commit } from './_common.mjs'
import { EXIT } from '../worker/thread.mjs'
import { satisfies } from 'semver'

export const type = pull.type

export const filter = [
...pull.filter,
(item) => item.name !== 'cli',
(item) => {
return (
item.pkg &&
Object.hasOwn(item.pkg.devDependencies || {}, '@npmcli/template-oss')
)
},
(item, _, __, opts) =>
item.pkg.templateOSS[opts.argv.configName] === 'true',
]

// Example usage (single repo):
// node ./bin/gh.mjs repos enable-lint-feature --filter "name:statusboard" --install --force --dryrun

// The defaults below are all for prettier, but you should be able to use this for linting features like no-unused-vars.
export const args = {
desc: 'Enable lint feature like prettier',
builder: flow(pull.args.builder, (yargs) =>
yargs.options({
configName: {
type: 'string',
default: 'prettier',
desc: 'template-oss config setting name',
},
templateOSSRange: {
type: 'string',
default: '>=4.23.0',
desc: 'required template-oss version range',
},
lintFixCommitMessage: {
type: 'string',
default: 'chore: run prettier',
desc: 'lintfix commit message',
},
prBody: {
type: 'string',
default: 'Enable and run prettier',
desc: 'PR body',
},
branchName: {
type: 'string',
default: 'stafftools/enable-prettier',
desc: 'branch name to use',
},
devDependencies: {
type: 'array',
// based on npm run postlint's output
default: ['prettier@*', 'eslint-config-prettier@*'],
desc: 'devDependencies to install',
},
exactDevDependencies: {
type: 'array',
// Todo: Add this to exactSpecs in template-oss as well
default: ['@github/prettier-config@0.0.6'],
desc: 'devDependencies to install with --save-exact',
},
install: {
type: 'boolean',
default: false,
desc: 'install deps',
},
force: {
type: 'boolean',
default: false,
desc: 'delete existing branch',
},
})
),
}

export const success = ({ state }) => state.success

export default [
({ argv, run, state }) => {
const url = run('gh', [
'pr',
'list',
'-L=1',
'-l=Dependencies',
`-S=templateOSS.${argv.configName}`,
'--json=url',
'-q',
'.[].url',
])
if (url) {
state.success = url
return ['echo', [], { status: () => EXIT }]
}
},
...pull.default,
({ argv }) => [
'npm',
['pkg', 'get', `templateOSS.${argv.configName}`],
{
status: ({ status, output }) => output.includes('true') ? EXIT : status,
},
],
({ argv }) => [
'npm',
['pkg', 'get', 'templateOSS.version'],
{
status: ({ status, output }) => {
if (status !== 0) {
return status
}
const version = output.replaceAll('"', '')
if (!satisfies(version, argv.templateOSSRange)) {
throw new Error(`template-oss version (${version}) doesn't satisfy the required range` +
` (${argv.templateOSSRange})`)
}
return 0
},
},
],
({ argv }) =>
argv.install && [
['npm', ['i']],
],
({ argv }) => argv.force && [
'git',
['branch', '-D', argv.branchName],
{
status: () => 0,
}],
({ argv }) => ['git', ['checkout', '-b', argv.branchName]],
({ argv }) => [
// Note: npm pkg set assigns a string value, not a boolean value
// We'll settle for truthy, instead of jumping through a lot of hoops
'npm',
['pkg', 'set', `templateOSS.${argv.configName}=true`],
],
() => [
'npx',
['template-oss-apply', '--force'],
],
({ argv }) => argv.devDependencies.length && [
// Q: Why aren't these required devDependencies installed by template-oss-apply --force?
'npm',
['i', ...argv.devDependencies, '--save-dev'],
],
({ argv }) => argv.exactDevDependencies.length && [
'npm',
['i', ...argv.exactDevDependencies, '--save-dev', '--save-exact'],
],
({ argv }) => {
return commit({
argv: { message: `chore: enable templateOSS.${argv.configName}` },
})
},
() => [
'npm',
['run', 'lintfix'],
],
({ argv }) => {
return commit({
argv: { message: argv.lintFixCommitMessage },
})
},
({ argv }) => !argv.dryrun && [
'git',
['push', argv.remote, argv.branchName, '--force-with-lease'],
],
({ argv, state }) => !argv.dryrun && [
'gh',
[
'pr',
'create',
'--title',
`"chore: Enable templateOSS.${argv.configName}"`,
'--body',
`"${argv.prBody}"`,
// '--draft',
'--label',
'Dependencies',
'--head',
argv.branchName,
'--base',
state.defaultBranch,
],
],
]
96 changes: 89 additions & 7 deletions tap-snapshots/test/gh.mjs.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ gh graphql add-template-oss
gh graphql clone
gh graphql comment
gh graphql delete-branches
gh graphql enable-lint-feature
gh graphql merge
gh graphql metrics
gh graphql pr-engines
Expand Down Expand Up @@ -53,6 +54,7 @@ gh repos
gh repos add-template-oss
gh repos clone
gh repos delete-branches
gh repos enable-lint-feature
gh repos metrics
gh repos publish-repo
gh repos pull
Expand Down Expand Up @@ -386,6 +388,7 @@ Commands:
npx -p @npmcli/stafftools gh graphql clone Clone repos into a directory
npx -p @npmcli/stafftools gh graphql comment Comment on pull requests
npx -p @npmcli/stafftools gh graphql delete-branches Delete branches of repos with no remote counterpart
npx -p @npmcli/stafftools gh graphql enable-lint-feature Enable lint feature like prettier
npx -p @npmcli/stafftools gh graphql merge Merge pull requests
npx -p @npmcli/stafftools gh graphql pr-engines Get engine changes in a pull request
npx -p @npmcli/stafftools gh graphql publish-release Merge pending release PRs and publish the resulting release
Expand Down Expand Up @@ -537,6 +540,44 @@ Other Options:
--config Path to JSON config file
`

exports[`test/gh.mjs TAP all commands help graphql enable-lint-feature > must match snapshot 1`] = `
npx -p @npmcli/stafftools gh graphql enable-lint-feature

Enable lint feature like prettier

Command Options:
--query path to a query file passed directly to gh api graphql [string] [required]
--cache how long for gh to cache the query [string] [default: "1m"]
--report shorthand for --template=report [boolean] [default: false]
--remote name of the remote [required] [default: "origin"]
--force delete existing branch [boolean] [default: false]
--sync whether to sync the repo [boolean] [default: true]
--configName template-oss config setting name [string] [default: "prettier"]
--templateOSSRange required template-oss version range [string] [default: ">=4.23.0"]
--lintFixCommitMessage lintfix commit message [string] [default: "chore: run prettier"]
--prBody PR body [string] [default: "Enable and run prettier"]
--branchName branch name to use [string] [default: "stafftools/enable-prettier"]
--devDependencies devDependencies to install [array] [default: ["prettier@*","eslint-config-prettier@*"]]
--exactDevDependencies devDependencies to install with --save-exact [array] [default: ["@github/prettier-config@0.0.6"]]
--install install deps [boolean] [default: false]

Global Options:
-c, --cwd base directory to run filesystem related commands [string] [default: "$HOME/projects"]
-l, --limit number of worker threads to spawn [number] [default: $NUM_CORES]
-f, --filter filters to be parsed as relaxed json and applied to the data [array]
-r, --reject rejectors to be parsed as relaxed json and applied to the data [array]
--clean whether to rimraf the cwd first [boolean] [default: false]
--template how to format the final output [string] [required] [choices: "json", "silent", "report"] [default: "report"]
--sort key to sort results by [string] [default: "id"]
--json shorthand for --template=json [boolean] [default: false]
--silent shorthand for --template=silent [boolean] [default: false]

Other Options:
--help Show help [boolean]
--version Show version number [boolean]
--config Path to JSON config file
`

exports[`test/gh.mjs TAP all commands help graphql merge > must match snapshot 1`] = `
npx -p @npmcli/stafftools gh graphql merge

Expand Down Expand Up @@ -1397,13 +1438,14 @@ npx -p @npmcli/stafftools gh repos
Fetch repos

Commands:
npx -p @npmcli/stafftools gh repos add-template-oss Add template-oss to a repo
npx -p @npmcli/stafftools gh repos clone Clone repos into a directory
npx -p @npmcli/stafftools gh repos delete-branches Delete branches of repos with no remote counterpart
npx -p @npmcli/stafftools gh repos publish-repo Publish repos from their default branch
npx -p @npmcli/stafftools gh repos pull Checkout and pull default branch of repos
npx -p @npmcli/stafftools gh repos repo-settings Set common settings on all repos
npx -p @npmcli/stafftools gh repos set-secret Set Publish Tokens
npx -p @npmcli/stafftools gh repos add-template-oss Add template-oss to a repo
npx -p @npmcli/stafftools gh repos clone Clone repos into a directory
npx -p @npmcli/stafftools gh repos delete-branches Delete branches of repos with no remote counterpart
npx -p @npmcli/stafftools gh repos enable-lint-feature Enable lint feature like prettier
npx -p @npmcli/stafftools gh repos publish-repo Publish repos from their default branch
npx -p @npmcli/stafftools gh repos pull Checkout and pull default branch of repos
npx -p @npmcli/stafftools gh repos repo-settings Set common settings on all repos
npx -p @npmcli/stafftools gh repos set-secret Set Publish Tokens

Command Options:
--cache how long for gh to cache the query [string] [default: "1h"]
Expand Down Expand Up @@ -1525,6 +1567,46 @@ Other Options:
--config Path to JSON config file
`

exports[`test/gh.mjs TAP all commands help repos enable-lint-feature > must match snapshot 1`] = `
npx -p @npmcli/stafftools gh repos enable-lint-feature

Enable lint feature like prettier

Command Options:
--cache how long for gh to cache the query [string] [default: "1h"]
--repos query to filter repos [string] [required] [default: "org:npm topic:npm-cli fork:true archived:false"]
--table shorthand for --template=table [boolean] [default: false]
--confirm shorthand for --template=confirm [boolean] [default: false]
--report shorthand for --template=report [boolean] [default: false]
--remote name of the remote [required] [default: "origin"]
--force delete existing branch [boolean] [default: false]
--sync whether to sync the repo [boolean] [default: true]
--configName template-oss config setting name [string] [default: "prettier"]
--templateOSSRange required template-oss version range [string] [default: ">=4.23.0"]
--lintFixCommitMessage lintfix commit message [string] [default: "chore: run prettier"]
--prBody PR body [string] [default: "Enable and run prettier"]
--branchName branch name to use [string] [default: "stafftools/enable-prettier"]
--devDependencies devDependencies to install [array] [default: ["prettier@*","eslint-config-prettier@*"]]
--exactDevDependencies devDependencies to install with --save-exact [array] [default: ["@github/prettier-config@0.0.6"]]
--install install deps [boolean] [default: false]

Global Options:
-c, --cwd base directory to run filesystem related commands [string] [default: "$HOME/projects"]
-l, --limit number of worker threads to spawn [number] [default: $NUM_CORES]
-f, --filter filters to be parsed as relaxed json and applied to the data [array]
-r, --reject rejectors to be parsed as relaxed json and applied to the data [array]
--clean whether to rimraf the cwd first [boolean] [default: false]
--template how to format the final output [string] [required] [choices: "json", "silent", "table", "confirm", "report"] [default: "report"]
--sort key to sort results by [string] [default: "id"]
--json shorthand for --template=json [boolean] [default: false]
--silent shorthand for --template=silent [boolean] [default: false]

Other Options:
--help Show help [boolean]
--version Show version number [boolean]
--config Path to JSON config file
`

exports[`test/gh.mjs TAP all commands help repos metrics > must match snapshot 1`] = `
npx -p @npmcli/stafftools gh repos metrics

Expand Down