Skip to content

Commit

Permalink
feat: added --ecosystem-preset to init
Browse files Browse the repository at this point in the history
  • Loading branch information
snorrees committed Aug 25, 2022
1 parent 25deacd commit e513afe
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 23 deletions.
5 changes: 4 additions & 1 deletion .releaserc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"extends": "@sanity/semantic-release-preset",
"branches": ["main"]
"branches": [
"main",
{"name": "ecosystem-preset", "channel": "ecosystem-preset", "prerelease": true}
]
}
85 changes: 85 additions & 0 deletions assets/splat/ecosystem/.github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI & Release
on:
# Build on pushes to release branches
push:
branches: [main]
# Build on pull requests targeting release branches
pull_request:
branches: [main]
workflow_dispatch:
inputs:
release:
description: Release new version
required: true
default: false
type: boolean

jobs:
log-the-inputs:
name: Log inputs
runs-on: ubuntu-latest
steps:
- run: |
echo "Inputs: $INPUTS"
env:
INPUTS: ${{ toJSON(inputs) }}
build:
name: Lint & Build
runs-on: ubuntu-latest
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npm run lint --if-present
- run: npm run prepublishOnly

test:
name: Test
needs: build
strategy:
matrix:
os: [ macos-latest, ubuntu-latest ]
node: [ lts/*, current ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test --if-present

release:
name: Semantic release
needs: test
runs-on: ubuntu-latest
# only run if opt-in during workflow_dispatch
if: inputs.release == true
steps:
- uses: actions/checkout@v3
with:
# Need to fetch entire commit history to
# analyze every commit since last release
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: lts/*
cache: npm
- run: npm ci
# Branches that will release new versions are defined in .releaserc.json
- run: npx semantic-release
# Don't allow interrupting the release step if the job is cancelled, as it can lead to an inconsistent state
# e.g. git tags were pushed but it exited before `npm publish`
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
4 changes: 4 additions & 0 deletions assets/splat/ecosystem/.husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no -- commitlint --edit ""
4 changes: 4 additions & 0 deletions assets/splat/ecosystem/.husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
4 changes: 4 additions & 0 deletions assets/splat/ecosystem/.releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@sanity/semantic-release-preset",
"branches": ["main"]
}
3 changes: 3 additions & 0 deletions assets/splat/ecosystem/commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
}
7 changes: 7 additions & 0 deletions assets/splat/ecosystem/renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>sanity-io/renovate-presets//ecosystem/auto",
"github>sanity-io/renovate-presets//ecosystem/studio-v3"
]
}
6 changes: 6 additions & 0 deletions src/actions/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {TypedFlags} from 'meow'
import {getPackage} from '../npm/package'
import {defaultSourceJs, defaultSourceTs} from '../configs/default-source'
import {incompatiblePluginPackage} from '../constants'
import {ecosystemDevDependencies} from '../ecosystem/ecosystem-preset'

export const initFlags = {
...sharedFlags,
Expand All @@ -33,6 +34,10 @@ export const initFlags = {
type: 'boolean',
default: true,
},
ecosystemPreset: {
type: 'boolean',
default: false,
},
gitignore: {
type: 'boolean',
default: true,
Expand Down Expand Up @@ -77,6 +82,7 @@ export async function init(options: InitOptions) {
devDependencies = {
...devDependencies,
...defaultDevDependencies,
...(options.flags.ecosystemPreset ? await ecosystemDevDependencies() : []),
...(await resolveLatestVersions(['rimraf'])),
}
peerDependencies = {
Expand Down
19 changes: 14 additions & 5 deletions src/actions/splat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '../util/files'
import {InitFlags} from './init'
import {PackageJson} from './verify/types'
import {ecosystemPresetFiles} from '../ecosystem/ecosystem-preset'

const bannedFields = ['login', 'description', 'projecturl', 'email']
const preferredLicenses = ['MIT', 'ISC', 'BSD-3-Clause']
Expand All @@ -29,6 +30,8 @@ const otherLicenses = Object.keys(licenses.list).filter((id) => {
)
})

export type FromTo = {from: string | string[]; to: string | string[]}

export interface SplatOptions {
basePath: string
requireUserConfirmation?: boolean
Expand Down Expand Up @@ -284,8 +287,6 @@ async function resolveProjectDescription(basePath: string, pkg: PackageJson | un
}
}

type FromTo = {from: string; to: string}

async function writeStaticAssets({basePath, flags}: SplatOptions) {
const assetsDir = await findAssetsDir()

Expand All @@ -300,18 +301,26 @@ async function writeStaticAssets({basePath, flags}: SplatOptions) {
flags.gitignore && {from: 'gitignore', to: '.gitignore'},
flags.typescript && {from: 'template-tsconfig.json', to: 'tsconfig.json'},
flags.prettier && {from: 'prettierrc.js', to: '.prettierrc.js'},
].filter((f): f is FromTo => !!f)

...(flags.ecosystemPreset ? ecosystemPresetFiles() : []),
].filter((f: false | FromTo): f is FromTo => !!f)

const writes: string[] = []
for (const file of files) {
if (await copyFileWithOverwritePrompt(from(file.from), to(file.to), flags)) {
writes.push(file.to)
const fromPath = asArray(file.from)
const toPath = asArray(file.to)
if (await copyFileWithOverwritePrompt(from(...fromPath), to(...toPath), flags)) {
writes.push(path.join(...toPath))
}
}

return writes
}

function asArray(input: string | string[]): string[] {
return typeof input === 'string' ? [input] : input
}

/**
* assets dir might be in higher or lower in the dir hierarchy depending on
* if we run from lib or src
Expand Down
2 changes: 2 additions & 0 deletions src/cmds/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Options
--no-scripts Disables scripts from being added to package.json
--no-install Disables automatically running package manager install
--ecosystem-preset [beta]: Adds opinionated files and dependencies for conventional-commits, githubworkflow, reonvatebot and semantic-release
--name [package-name] Use the provided package-name
--author [name] Use the provided author
--repo [url] Use the provided repo url
Expand Down
32 changes: 32 additions & 0 deletions src/ecosystem/ecosystem-preset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {FromTo} from '../actions/splat'
import {resolveLatestVersions} from '../npm/resolveLatestVersions'

export function ecosystemPresetFiles(): FromTo[] {
return [
{
from: ['ecosystem', '.github', 'workflows', 'main.yml'],
to: ['.github', 'workflows', 'main.yml'],
},
{
from: ['ecosystem', '.husky', 'commit-msg'],
to: ['.husky', 'commit-msg'],
},
{
from: ['ecosystem', '.husky', 'pre-commit'],
to: ['.husky', 'pre-commit'],
},
{from: ['ecosystem', '.releaserc.json'], to: '.releaserc.json'},
{from: ['ecosystem', 'commitlint.config.js'], to: 'commitlint.config.js'},
{from: ['ecosystem', 'renovate.json'], to: 'renovate.json'},
]
}

export async function ecosystemDevDependencies(): Promise<Record<string, string>> {
return resolveLatestVersions([
'@commitlint/cli',
'@commitlint/config-conventional',
'@sanity/semantic-release-preset',
'husky',
'lint-staged',
])
}
11 changes: 11 additions & 0 deletions src/util/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ export async function copyFileWithOverwritePrompt(from: string, to: string, flag
return false
}

await ensureDirectoryExists(to)

if (
!flags.force &&
(await fileExists(to)) &&
Expand All @@ -161,6 +163,15 @@ export async function copyFileWithOverwritePrompt(from: string, to: string, flag
return true
}

async function ensureDirectoryExists(filePath: string) {
const dirname = path.dirname(filePath)
if (await fileExists(dirname)) {
return true
}
await ensureDirectoryExists(dirname)
await mkdir(dirname)
}

export async function fileEqualsData(filePath: string, content: string) {
const contentHash = crypto.createHash('sha1').update(content).digest('hex')
const remoteHash = await getFileHash(filePath)
Expand Down
76 changes: 59 additions & 17 deletions test/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ import {fileExists} from '../src/util/files'
import {incompatiblePluginPackage} from '../src/constants'
import {PackageJson} from '../src/actions/verify/types'

const defaultDevDependencies = [
'@sanity/plugin-kit',
'@typescript-eslint/eslint-plugin',
'@typescript-eslint/parser',
'eslint',
'eslint-config-prettier',
'eslint-config-sanity',
'eslint-plugin-prettier',
'eslint-plugin-react',
'eslint-plugin-react-hooks',
'parcel',
'prettier',
'react',
'rimraf',
'sanity',
'typescript',
]

tap.test('plugin-kit init --force in empty directory', async (t) => {
await testFixture({
fixturePath: 'init/empty',
Expand Down Expand Up @@ -98,25 +116,10 @@ tap.test('plugin-kit init --force in empty directory', async (t) => {
['react', 'sanity'],
'should have expected peerDependencies'
)

t.strictSame(
Object.keys(pkg.devDependencies ?? {}),
[
'@sanity/plugin-kit',
'@typescript-eslint/eslint-plugin',
'@typescript-eslint/parser',
'eslint',
'eslint-config-prettier',
'eslint-config-sanity',
'eslint-plugin-prettier',
'eslint-plugin-react',
'eslint-plugin-react-hooks',
'parcel',
'prettier',
'react',
'rimraf',
'sanity',
'typescript',
],
defaultDevDependencies,
'should have expected devDependencies'
)
},
Expand Down Expand Up @@ -178,3 +181,42 @@ tap.test('plugin-kit init --force with all the opt-outs in empty directory', asy
},
})
})

tap.test('plugin-kit init --force --ecosystem-preset in empty directory', async (t) => {
await testFixture({
fixturePath: 'init/empty',
relativeOutPath: 'defaults-ecosystem',
command: ({outputDir}) =>
runCliCommand('init', [outputDir, ...initTestArgs, '--ecosystem-preset']),
assert: async ({result: {stdout, stderr}, outputDir}) => {
t.equal(stderr, '', 'should have empty stderr')

const fileContains = fileContainsValidator(t, outputDir)

await fileContains(path.join('.github', 'workflows', 'main.yml'), 'CI & Release')
await fileContains(path.join('.husky', 'commit-msg'), 'npx --no -- commitlint')
await fileContains(path.join('.husky', 'pre-commit'), 'npx lint-staged')
await fileContains(path.join('.releaserc.json'), '@sanity/semantic-release-preset')
await fileContains(path.join('commitlint.config.js'), '@commitlint/config-conventional')
await fileContains(
path.join('renovate.json'),
'github>sanity-io/renovate-presets//ecosystem/auto'
)

const pkg: PackageJson = JSON.parse(await readFile(path.join(outputDir, 'package.json')))

t.strictSame(
Object.keys(pkg.devDependencies ?? {}),
[
...defaultDevDependencies,
'@commitlint/cli',
'@commitlint/config-conventional',
'@sanity/semantic-release-preset',
'husky',
'lint-staged',
].sort(),
'should have expected devDependencies'
)
},
})
})

0 comments on commit e513afe

Please sign in to comment.