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: add support to custom repository info #100

Merged
merged 32 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
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
6 changes: 5 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
ignorePatterns: ['dist/', 'node_modules/'],
ignorePatterns: ['dist/', 'node_modules/', '.eslintrc.cjs'],
env: {
node: true,
es2021: true,
Expand All @@ -8,12 +8,16 @@ module.exports = {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
},
plugins: ['@typescript-eslint'],
extends: ['eslint:recommended', 'prettier', 'plugin:@typescript-eslint/recommended'],
root: true,
overrides: [],
rules: {
'no-return-await': 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
// We need to ignore unused variables that start with an underscore to avoid linting errors on catch(error) blocks
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,7 @@ results.json
dist/

# Ignore test fixtures
test/fixtures/fake-remote
test/fixtures/project-one
test/fixtures/super-project-source
test/fixtures/super-project-remote
test/fixtures/empty-project-source
test/fixtures/empty-project-remote
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"sort-imports.on-save": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"typescript.preferences.importModuleSpecifier": "relative"
}
2 changes: 1 addition & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# precedence. When someone opens a pull request that only
# modifies JS files, only these and not the global
# owner(s) will be requested for a review.
*.js @javascript
*.test.ts @test

# In this example, the user owns any files in the src directory
# at the root of the repository and any of its subdirectories.
Expand Down
14 changes: 8 additions & 6 deletions bin/commands/diff.js → bin/commands/diff.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import * as git from '../../src/git.js'

import { Occurrence, OutputFile } from '../../src/types.js'
import { allowMultipleValues, countByMetric } from '../helpers.js'

import Codeowners from '../../src/codeowners.js'
import { Command } from 'commander'
import _ from 'lodash'
import { allowMultipleValues } from './run.js'
import { countByMetric } from '../helpers.js'
import { findOccurrences } from '../../src/occurrences.js'
import fs from 'fs'
import { getConfiguration } from '../../src/configuration.js'
import { getFiles } from '../../src/files.js'
import { panic } from '../../src/error.js'

export default function (program) {
export default function (program: Command) {
program
.command('diff')
.requiredOption('--metric <metric>', 'will only consider provided metrics', allowMultipleValues)
Expand All @@ -23,7 +25,7 @@ export default function (program) {
const inputFile = options.inputFile

let lastMetricValue
let previousOccurrences
let previousOccurrences: Occurrence[] = []
let metricOccurrences

const initialBranch = await git.branchName()
Expand Down Expand Up @@ -66,8 +68,8 @@ export default function (program) {

if (inputFile) {
const content = fs.readFileSync(inputFile, 'utf8')
const metrics = JSON.parse(content)
metricOccurrences = metrics.find((m) => m.name === metricName)?.occurrences || []
const metrics: OutputFile = JSON.parse(content)
metricOccurrences = metrics.find((metric) => metric.name === metricName)?.occurrences || []
previousOccurrences = metricOccurrences
lastMetricValue = _.sumBy(metricOccurrences, (occurrence) =>
_.isNumber(occurrence.value) ? occurrence.value : 1
Expand Down
19 changes: 9 additions & 10 deletions bin/commands/init.js → bin/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@

import * as git from '../../src/git.js'

import {
createConfigurationFile,
createWorkflowFile,
getConfigurationFile,
workflowExists,
} from '../../src/configuration.js'
import { createConfigurationFile, createWorkflowFile, getConfigFile, workflowExists } from '../../src/configuration.js'

import { Command } from 'commander'
import prompt from 'prompt'

export default function (program) {
export default function (program: Command) {
program.command('init').action(async () => {
// If the configuration file already exists, don't allow the user to run the init command
const configurationFile = getConfigurationFile()
const configurationFile = getConfigFile()
if (configurationFile) {
console.error(`${configurationFile} already exists.`)
process.exit(1)
Expand All @@ -23,15 +19,18 @@ export default function (program) {
prompt.message = ''
prompt.start()

const remoteUrl = await git.getRemoteUrl()
const remoteUrl = await git.gitRemoteUrl()
let projectName = git.guessProjectName(remoteUrl)

if (projectName === null) {
const { repo } = await prompt.get({
properties: { repo: { message: 'Enter your project name', required: true } },
})
projectName = repo
if (typeof repo === 'string') projectName = repo
}

if (!projectName) throw new Error('Project name is required')

createConfigurationFile(projectName)

if (!workflowExists()) createWorkflowFile()
Expand Down
42 changes: 20 additions & 22 deletions bin/commands/push.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import * as git from '../../src/git.js'

import { upload, uploadContributions } from '../helpers.js'
import { upload } from '../helpers.js'

import { Command } from 'commander'
import Codeowners from '../../src/codeowners.js'
import { computeContributions } from '../../src/contributions.js'
import { findOccurrences } from '../../src/occurrences.js'
import { getConfiguration } from '../../src/configuration.js'
import { getFiles } from '../../src/files.js'
import { computeContributions, uploadContributions } from '../../src/contributions.js'
import { panic } from '../../src/error.js'
import { getFiles } from '../../src/files.js'
import { findOccurrences } from '../../src/occurrences.js'

// @ts-expect-error TODO: properly type this
export default function (program) {
export default function (program: Command) {
program
.command('push')
.option('--api-key <api_key>', 'your cherrypush.com API key')
.option('--quiet', 'reduce output to a minimum')
// @ts-expect-error TODO: properly type this
.action(async (options) => {
const sha = await git.sha()
const configuration = await getConfiguration()
const initialBranch = await git.branchName()
if (!initialBranch) panic('Not on a branch, checkout a branch before pushing metrics.')
const sha = await git.sha()

const hasUncommitedChanges = (await git.uncommittedFiles()).length > 0
if (hasUncommitedChanges) panic('Please commit your changes before running cherry push.')

const apiKey = options.apiKey || process.env.CHERRY_API_KEY
if (!apiKey) panic('Please provide an API key with --api-key or CHERRY_API_KEY environment variable')

let error
try {
console.log('Computing metrics for current commit...')
const occurrences = await findOccurrences({
Expand All @@ -37,8 +38,7 @@ export default function (program) {

await upload(apiKey, configuration.project_name, await git.commitDate(sha), occurrences)

console.log('')
console.log('Computing metrics for previous commit...')
console.log('\nComputing metrics for previous commit...')
await git.checkout(`${sha}~`)
const previousOccurrences = await findOccurrences({
configuration,
Expand All @@ -50,27 +50,25 @@ export default function (program) {
const contributions = computeContributions(occurrences, previousOccurrences)

if (contributions.length) {
console.log(` Uploading contributions...`)
console.log('\nUploading contributions...')
await uploadContributions(
apiKey,
configuration.project_name,
await git.authorName(sha),
await git.authorEmail(sha),
sha,
await git.commitDate(sha),
contributions
contributions,
configuration.repository
)
} else console.log('No contribution found, skipping')
} catch (exception) {
error = exception
} finally {
git.checkout(initialBranch)
}
if (error) {

await git.checkout(initialBranch)
console.log(`Your dashboard is available at https://www.cherrypush.com/user/projects`)
} catch (error) {
console.error(error)
process.exit(1)
process.exitCode = 1
await git.checkout(initialBranch)
}

console.log(`Your dashboard is available at https://www.cherrypush.com/user/projects`)
})
}
39 changes: 24 additions & 15 deletions bin/commands/run.js → bin/commands/run.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import * as git from '../../src/git.js'

import {
buildMetricsPayload,
buildSarifPayload,
buildSonarGenericImportPayload,
countByMetric,
sortObject,
} from '../helpers.js'
import { allowMultipleValues, buildMetricsPayload, countByMetric, sortObject } from '../helpers.js'

import Codeowners from '../../src/codeowners.js'
import _ from 'lodash'
import { findOccurrences } from '../../src/occurrences.js'
import { Command } from 'commander'
import fs from 'fs'
import _ from 'lodash'
import Codeowners from '../../src/codeowners.js'
import { getConfiguration } from '../../src/configuration.js'
import { getFiles } from '../../src/files.js'
import { panic } from '../../src/error.js'
import { getFiles } from '../../src/files.js'
import { buildSarifPayload } from '../../src/helpers/sarif.js'
import { buildSonarGenericImportPayload } from '../../src/helpers/sonar.js'
import { findOccurrences } from '../../src/occurrences.js'

export const allowMultipleValues = (value, previous) => (previous ? [...previous, value] : [value])
const ALLOWED_FORMATS = ['json', 'sarif', 'sonar'] as const

export default function (program) {
export default function (program: Command) {
program
.command('run')
.option('--owner <owner>', 'will only consider the provided code owners', allowMultipleValues)
Expand Down Expand Up @@ -54,20 +51,32 @@ export default function (program) {
if (options.output) {
const filepath = process.cwd() + '/' + options.output
const format = options.format || 'json'
let content
let content: string | undefined

if (!ALLOWED_FORMATS.includes(format)) {
panic(`Invalid format provided: ${format}`)
console.log(`Allowed formats: ${ALLOWED_FORMATS.join(', ')}`)
return
}

if (format === 'json') {
const metrics = buildMetricsPayload(occurrences)
content = JSON.stringify(metrics, null, 2)
} else if (format === 'sarif') {
const branch = await git.branchName()
const sha = await git.sha()
const sarif = buildSarifPayload(configuration.project_name, branch, sha, occurrences)
const sarif = buildSarifPayload(configuration.repository, branch, sha, occurrences)
content = JSON.stringify(sarif, null, 2)
} else if (format === 'sonar') {
const sonar = buildSonarGenericImportPayload(occurrences)
content = JSON.stringify(sonar, null, 2)
}
Comment on lines 62 to 73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another way to do this is by doing a mapping, like this :

const buildPayloadMapping: Record<keyof ALLOWED_FORMATS, () => object> = {
  json: () => buildMetricsPayload(occurences),
  sonar: () => buildSonarGenericImportPayload(occurences),
  sarif: async () => { 
    const branch = await git.branchName()
    const sha = await git.sha()
    return buildSarifPayload(...)
  }
}

const content = JSON.stringify(await buildPayloadMapping[format], null, 2)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time with this one because some of the mappings are async while others are not. I'll make this change in a follow-up PR.


if (!content) {
panic('Error while generating content')
return
}

fs.writeFile(filepath, content, 'utf8', function (err) {
if (err) panic(err)
console.log(`File has been saved as ${filepath}`)
Expand Down
Loading