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

Improve the init CLI #1436

Merged
merged 3 commits into from
Apr 16, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
<!-- Your comment below this -->
<!-- Your comment above this -->


## 12.1.0

It's been 7 years since I looked at `danger init` and err, the world of CI has changed quite a bit since then. So, Danger JS's
`init` command now knows that GitHub Actions exists and will correctly offer some advice on how to set up a Dangerfile for it. - [@orta]

## 12.0.0

Bumping to 12.x because we've raised the minimum to node version from 14 to 18. This is due to some of our dependencies
Expand Down
45 changes: 25 additions & 20 deletions source/commands/danger-init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import program from "commander"
import * as fs from "fs"

import { generateDefaultDangerfile } from "./init/default-dangerfile"
import { travis, circle, unsure } from "./init/add-to-ci"
import { travis, circle, unsure, githubActions } from "./init/add-to-ci"
import { generateInitialState, createUI } from "./init/state-setup"
import { InitUI, InitState, highlight } from "./init/interfaces"

Expand Down Expand Up @@ -42,9 +42,15 @@ const go = async (app: App) => {
state.isAnOSSRepo = isOSS

await setupDangerfile(ui, state)
await setupGitHubAccount(ui, state)
await setupGHAccessToken(ui, state)
await addToCI(ui, state)

if (state.isAnOSSRepo) {
await setupGitHubAccount(ui, state)
await setupGHAccessToken(ui, state)
await addToCI(ui, state)
} else {
// We can use the private github workflow for private repos
await githubActions(ui, state)
}
await wrapItUp(ui, state)
await thanks(ui, state)
}
Expand All @@ -65,9 +71,9 @@ const showTodoState = async (ui: InitUI) => {
await ui.pause(0.6)
ui.say(` - [ ] Create a Dangerfile and add a few simple rules.`)
await ui.pause(0.6)
ui.say(` - [ ] Create a GitHub account for Danger to use, for messaging.`)
ui.say(` - [ ] Potentially create a GitHub account for Danger to use, for messaging.`)
await ui.pause(0.6)
ui.say(` - [ ] Set up an access token for Danger.`)
ui.say(` - [ ] Set up an access token for Danger to comment with.`)
await ui.pause(0.6)
ui.say(" - [ ] Set up Danger to run on your CI.\n")

Expand Down Expand Up @@ -202,21 +208,20 @@ const wrapItUp = async (ui: InitUI, _state: InitState) => {
await ui.pause(0.6)

const link = (name: string, url: string) => ui.say(" * " + ui.link(name, url))
link("artsy/Emission#dangerfile.ts", "https://github.com/artsy/emission/blob/master/dangerfile.ts")
link("artsy/eigen#dangerfile.ts", "https://github.com/artsy/eigen/blob/master/dangerfile.ts")
link(
"facebook/react-native#danger/dangerfile.js",
"https://github.com/facebook/react-native/blob/master/bots/dangerfile.js"
"facebook/react-native#main/packages/react-native-bots/dangerfile.js",
"https://github.com/facebook/react-native/blob/main/packages/react-native-bots/dangerfile.js"
)
link("mui/material-ui#dangerfile.ts", "https://github.com/mui/material-ui/blob/main/dangerfile.ts#L4")
link(
"apollographql/apollo-client#dangerfile.ts",
"https://github.com/apollographql/apollo-client/blob/master/config/dangerfile.ts"
"styleguidist/react-styleguidist#dangerfile.ts",
"https://github.com/styleguidist/react-styleguidist/blob/master/dangerfile.ts"
)
link(
"styleguidist/react-styleguidist#dangerfile.js",
"https://github.com/styleguidist/react-styleguidist/blob/master/dangerfile.js"
"storybooks/storybook#.ci/danger/dangerfile.ts",
"https://github.com/storybookjs/storybook/blob/master/.ci/danger/dangerfile.ts"
)
link("storybooks/storybook#dangerfle.js", "https://github.com/storybooks/storybook/blob/master/dangerfile.js")
link("ReactiveX/rxjs#dangerfle.js", "https://github.com/ReactiveX/rxjs/blob/master/dangerfile.js")

await ui.pause(1)
}
Expand All @@ -225,7 +230,9 @@ const addToCI = async (ui: InitUI, state: InitState) => {
ui.header("Add to CI")

await ui.pause(0.6)
if (state.ciType === "travis") {
if (state.ciType === "gh-actions") {
await githubActions(ui, state)
} else if (state.ciType === "travis") {
await travis(ui, state)
} else if (state.ciType === "circle") {
await circle(ui, state)
Expand All @@ -242,10 +249,8 @@ const thanks = async (ui: InitUI, _state: InitState) => {
ui.say("and every who has sent PRs.\n")
ui.say(
"If you like Danger, let others know. If you want to know more, follow " +
highlight("@orta") +
" and " +
highlight("@DangerSystems") +
" on Twitter."
highlight("@orta@webtoo.ls") +
" on Mastodon!"
)
ui.say("If you don't like something about Danger, help us improve the project - it's all done on volunteer time! xxx")
ui.say("Remember: it's nice to be nice.\n")
Expand Down
60 changes: 60 additions & 0 deletions source/commands/init/add-to-ci.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,64 @@
import { InitUI, InitState, highlight } from "./interfaces"
import chalk from "chalk"

export const githubActions = async (ui: InitUI, state: InitState) => {
if (!state.isAnOSSRepo) {
ui.say("For your closed-source project, we can use the default GitHub Auth token for posting.")
ui.say("So, you don't need to create a bot account.")
ui.pause(0.5)

ui.say("You will want to add a new step in an existing workflow yaml file.")
ui.pause(0.2)

ui.say("The step should look like this:")
ui.say("")
ui.say("```yaml")
ui.say(chalk.bold(" - name: Run Danger"))
ui.say(chalk.bold(" run: yarn danger ci"))
ui.say(chalk.bold(" env:"))
ui.say(chalk.bold(" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`"))
ui.say("```")
ui.say("")
ui.pause(1)

ui.say("This will run Danger on every PR, press return when you have added this to one of your workflows.")
ui.waitForReturn()
return
}

const link = ui.link("issue #918.", "https://github.com/danger/danger-js/issues/918")
ui.say(
`For your OSS project, there are a few options you can find in ${link}. We will continue with using the new GitHub user account here.`
)
ui.pause(0.5)

ui.say("For your OSS project, you need to add a new secret to your workflow in a way that is slightly obfuscated.")
ui.say("This is because GitHub Actions doesn't allow you to use the default GITHUB_TOKEN to update its own comments.")
ui.pause(0.5)
ui.say("This would make Danger very spammy on a Pull Request.")

ui.pause(0.1)
ui.say("To get started, add a new step in an existing workflow file.")
ui.say("The step should look like this:")
ui.say("")
ui.say("```yaml")
ui.say(
chalk.bold(`)
- name: Run Danger
run: |
# Exposing this token is safe because the user of it has no other public repositories
# and has no permission to modify this repository.
TOKEN='ghp_On3bHOkcV2AmVY6'
TOKEN+='56186c3b09b4f86b1c65136769'
DANGER_GITHUB_API_TOKEN=$TOKEN yarn danger ci`)
)
ui.say("```")
ui.say("")
ui.pause(1)

ui.say("This will run Danger on every PR, press return when you have added this to one of your workflows.")
ui.waitForReturn()
}

export const travis = async (ui: InitUI, state: InitState) => {
// https://travis-ci.org/artsy/eigen/settings
Expand Down
10 changes: 7 additions & 3 deletions source/commands/init/default-dangerfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,21 @@ export const generateDefaultDangerfile = (state: InitState) => {

${rules.join("\n")}
`
return formatDangerfile(dangerfile, dangerfileState)
return formatDangerfile(dangerfile, state, dangerfileState)
}

export const formatDangerfile = (dangerfile: string, dangerfileState: any) => {
export const formatDangerfile = (
dangerfile: string,
initState: InitState,
dangerfileState: ReturnType<typeof generateDangerfileState>
) => {
if (dangerfileState.hasPrettier) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { format } = require("prettier")
// Get package settings
const localPrettier = fs.existsSync("package.json") && JSON.parse(fs.readFileSync("package.json", "utf8")).prettier
// Always include this
const always = { editorconfig: true }
const always = { editorconfig: true, parser: "typescript", filepath: process.cwd() + " /" + initState.filename }
const settings = localPrettier ? { ...always, ...localPrettier } : always

return format(dangerfile, settings)
Expand Down
2 changes: 1 addition & 1 deletion source/commands/init/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface InitState {
hasSetUpAccountToken: boolean

repoSlug: string | null
ciType: "travis" | "circle" | "unknown"
ciType: "gh-actions" | "travis" | "circle" | "unknown"
isGitHub: boolean
}

Expand Down
4 changes: 3 additions & 1 deletion source/commands/init/state-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export const generateInitialState = (osProcess: NodeJS.Process): InitState => {
const isBabel = checkForBabel()
const hasTravis = fs.existsSync(".travis.yml")
const hasCircle = fs.existsSync("circle.yml")
const ciType = hasTravis ? "travis" : hasCircle ? "circle" : "unknown"
const hasGitHubActions = fs.existsSync(".github/") && fs.existsSync(".github/workflows")

const ciType = hasGitHubActions ? "gh-actions" : hasTravis ? "travis" : hasCircle ? "circle" : "unknown"
const repoSlug = getRepoSlug()
const isGitHub = !!repoSlug

Expand Down