Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use git remote for branch related config #330

Merged
merged 1 commit into from
Jul 10, 2023
Merged
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
13 changes: 1 addition & 12 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,7 @@ updates:
directory: /
schedule:
interval: daily
allow:
- dependency-type: direct
versioning-strategy: increase-if-necessary
commit-message:
prefix: deps
prefix-development: chore
labels:
- "Dependencies"
- package-ecosystem: npm
directory: workspace/test-workspace/
schedule:
interval: daily
target-branch: "main"
allow:
- dependency-type: direct
versioning-strategy: increase-if-necessary
Expand Down
28 changes: 0 additions & 28 deletions .github/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,3 @@ branches:
apps: []
users: []
teams: [ "cli-team" ]
- name: latest
protection:
required_status_checks: null
enforce_admins: true
block_creations: true
required_pull_request_reviews:
required_approving_review_count: 1
require_code_owner_reviews: true
require_last_push_approval: true
dismiss_stale_reviews: true
restrictions:
apps: []
users: []
teams: [ "cli-team" ]
- name: release/v*
protection:
required_status_checks: null
enforce_admins: true
block_creations: true
required_pull_request_reviews:
required_approving_review_count: 1
require_code_owner_reviews: true
require_last_push_approval: true
dismiss_stale_reviews: true
restrictions:
apps: []
users: []
teams: [ "cli-team" ]
2 changes: 0 additions & 2 deletions .github/workflows/ci-test-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ on:
push:
branches:
- main
- latest
- release/v*
paths:
- workspace/test-workspace/**
schedule:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ on:
push:
branches:
- main
- latest
- release/v*
paths-ignore:
- workspace/test-workspace/**
schedule:
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@ on:
push:
branches:
- main
- latest
- release/v*
pull_request:
branches:
- main
- latest
- release/v*
schedule:
# "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1
- cron: "0 10 * * 1"
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ on:
push:
branches:
- main
- latest
- release/v*

permissions:
contents: write
Expand Down
20 changes: 18 additions & 2 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const { relative, dirname, join, extname, posix, win32 } = require('path')
const { defaults, pick, omit, uniq } = require('lodash')
const semver = require('semver')
const parseCIVersions = require('./util/parse-ci-versions.js')
const getGitUrl = require('./util/get-git-url.js')
const parseDependabot = require('./util/dependabot.js')
const git = require('./util/git.js')
const gitignore = require('./util/gitignore.js')
const { mergeWithArrays } = require('./util/merge.js')
const { FILE_KEYS, parseConfig: parseFiles, getAddedFiles, mergeFiles } = require('./util/files.js')
Expand All @@ -11,6 +12,7 @@ const CONFIG_KEY = 'templateOSS'
const getPkgConfig = (pkg) => pkg[CONFIG_KEY] || {}

const { name: NAME, version: LATEST_VERSION } = require('../package.json')
const { minimatch } = require('minimatch')
const MERGE_KEYS = [...FILE_KEYS, 'defaultContent', 'content']
const DEFAULT_CONTENT = require.resolve(NAME)

Expand Down Expand Up @@ -153,6 +155,12 @@ const getFullConfig = async ({
const publicPkgs = pkgs.filter(p => !p.pkgJson.private)
const allPrivate = pkgs.every(p => p.pkgJson.private)

const branches = uniq([...pkgConfig.branches ?? [], pkgConfig.releaseBranch]).filter(Boolean)
const gitBranches = await git.getBranches(rootPkg.path, branches)
const currentBranch = await git.currentBranch(rootPkg.path)
const isReleaseBranch = currentBranch ? minimatch(currentBranch, pkgConfig.releaseBranch) : false
const defaultBranch = await git.defaultBranch(rootPkg.path) ?? 'main'

// all derived keys
const derived = {
isRoot,
Expand All @@ -170,6 +178,14 @@ const getFullConfig = async ({
allPrivate,
// controls whether we are in a monorepo with any public workspaces
isMonoPublic: isMono && !!publicPkgs.filter(p => p.path !== rootPkg.path).length,
// git
defaultBranch,
baseBranch: isReleaseBranch ? currentBranch : defaultBranch,
branches: gitBranches.branches,
branchPatterns: gitBranches.patterns,
isReleaseBranch,
// dependabot
dependabot: parseDependabot(pkgConfig, defaultConfig, gitBranches.branches),
// repo
repoDir: rootPkg.path,
repoFiles,
Expand Down Expand Up @@ -261,7 +277,7 @@ const getFullConfig = async ({
}
}

const gitUrl = await getGitUrl(rootPkg.path)
const gitUrl = await git.getUrl(rootPkg.path)
if (gitUrl) {
derived.repository = {
type: 'git',
Expand Down
2 changes: 1 addition & 1 deletion lib/content/_on-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pull_request:
{{/if}}
push:
branches:
{{#each branches}}
{{#each branchPatterns}}
- {{ . }}
{{/each}}
{{#if isWorkspace}}
Expand Down
2 changes: 1 addition & 1 deletion lib/content/ci-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
ref:
required: true
type: string
default: {{ defaultBranch }}
default: {{ baseBranch }}
workflow_call:
inputs:
ref:
Expand Down
4 changes: 2 additions & 2 deletions lib/content/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ name: CodeQL
on:
push:
branches:
{{#each branches}}
{{#each branchPatterns}}
- {{ . }}
{{/each}}
pull_request:
branches:
{{#each branches}}
{{#each branchPatterns}}
- {{ . }}
{{/each}}
schedule:
Expand Down
13 changes: 11 additions & 2 deletions lib/content/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
version: 2

updates:
{{#each dependabot}}
- package-ecosystem: npm
directory: {{ pkgDir }}
directory: /
schedule:
interval: daily
target-branch: "{{ branch }}"
allow:
- dependency-type: direct
versioning-strategy: {{ dependabot }}
{{#each allowNames }}
dependency-name: "{{ . }}"
{{/each}}
versioning-strategy: {{ strategy }}
commit-message:
prefix: deps
prefix-development: chore
labels:
- "Dependencies"
{{#each labels }}
- "{{ . }}"
{{/each}}
{{/each}}
16 changes: 6 additions & 10 deletions lib/content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,24 @@ const sharedRootAdd = (name) => ({
'.github/dependabot.yml': {
file: 'dependabot.yml',
filter: (p) => p.config.dependabot,
clean: (p) => p.config.isRoot,
// dependabot takes a single top level config file. this parser
// will run for all configured packages and each one will have
// its item replaced in the updates array based on the directory
parser: (p) => class extends p.YmlMerge {
key = 'updates'
id = 'directory'
},
},
'.github/workflows/post-dependabot.yml': {
file: 'post-dependabot.yml',
filter: (p) => p.config.dependabot,
},
'.github/settings.yml': {
file: 'settings.yml',
filter: (p) => !p.config.isReleaseBranch,
},
})

const sharedRootRm = () => ({
'.github/workflows/pull-request.yml': {
filter: (p) => p.config.allPrivate,
},
'.github/settings.yml': {
filter: (p) => p.config.isReleaseBranch,
},
})

// Changes applied to the root of the repo
Expand Down Expand Up @@ -139,8 +135,8 @@ module.exports = {
workspaceModule,
windowsCI: true,
macCI: true,
branches: ['main', 'latest', 'release/v*'],
defaultBranch: 'main',
branches: ['main', 'latest'],
releaseBranch: 'release/v*',
distPaths: [
'bin/',
'lib/',
Expand Down
2 changes: 1 addition & 1 deletion lib/content/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
type: string
push:
branches:
{{#each branches}}
{{#each branchPatterns}}
- {{ . }}
{{/each}}

Expand Down
27 changes: 27 additions & 0 deletions lib/util/dependabot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const { name: NAME } = require('../../package.json')
const { minimatch } = require('minimatch')

const parseDependabotConfig = (v) => typeof v === 'string' ? { strategy: v } : (v ?? {})

module.exports = (config, defaultConfig, branches) => {
const { dependabot } = config
const { dependabot: defaultDependabot } = defaultConfig

if (!dependabot) {
return false
}

return branches
.filter((b) => dependabot[b] !== false)
.map(branch => {
const isReleaseBranch = minimatch(branch, config.releaseBranch)
return {
branch,
allowNames: isReleaseBranch ? [NAME] : [],
labels: isReleaseBranch ? ['Backport', branch] : [],
...parseDependabotConfig(defaultDependabot),
...parseDependabotConfig(dependabot),
...parseDependabotConfig(dependabot[branch]),
}
})
}
26 changes: 0 additions & 26 deletions lib/util/get-git-url.js

This file was deleted.

82 changes: 82 additions & 0 deletions lib/util/git.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const hgi = require('hosted-git-info')
const git = require('@npmcli/git')
const { minimatch } = require('minimatch')

const cache = new Map()

const tryGit = async (path, ...args) => {
if (!await git.is({ cwd: path })) {
throw new Error('no git')
}
const key = [path, ...args].join(',')
if (cache.has(key)) {
return cache.get(key)
}
const res = git.spawn(args, { cwd: path }).then(r => r.stdout.trim())
cache.set(key, res)
return res
}

// parse a repo from a git origin into a format
// for a package.json#repository object
const getUrl = async (path) => {
try {
const urlStr = await tryGit(path, 'remote', 'get-url', 'origin')
const { domain, user, project } = hgi.fromUrl(urlStr)
const url = new URL(`https://${domain}`)
url.pathname = `/${user}/${project}.git`
return url.toString()
} catch {
// errors are ignored
}
}

const getBranches = async (path, branchPatterns) => {
let matchingBranches = new Set()
let matchingPatterns = new Set()

try {
const res = await tryGit(path, 'ls-remote', '--heads', 'origin').then(r => r.split('\n'))
const remotes = res.map((h) => h.match(/refs\/heads\/(.*)$/)).filter(Boolean).map(h => h[1])
for (const branch of remotes) {
for (const pattern of branchPatterns) {
if (minimatch(branch, pattern)) {
matchingBranches.add(branch)
matchingPatterns.add(pattern)
}
}
}
} catch {
matchingBranches = new Set(branchPatterns.filter(b => !b.includes('*')))
matchingPatterns = new Set(branchPatterns)
}

return {
branches: [...matchingBranches],
patterns: [...matchingPatterns],
}
}

const defaultBranch = async (path) => {
try {
const remotes = await tryGit(path, 'remote', 'show', 'origin')
return remotes.match(/HEAD branch: (.*)$/m)?.[1]
} catch {
// ignore errors
}
}

const currentBranch = async (path) => {
try {
return await tryGit(path, 'rev-parse', '--abbrev-ref', 'HEAD')
} catch {
// ignore errors
}
}

module.exports = {
getUrl,
getBranches,
defaultBranch,
currentBranch,
}
Loading