- 
                Notifications
    You must be signed in to change notification settings 
- Fork 16
feat(nx-plugin): add general executor logic #740
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
          
     Merged
      
      
    
  
     Merged
                    Changes from 11 commits
      Commits
    
    
            Show all changes
          
          
            21 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      b7c24e6
              
                feat(nx-plugin): add general executor logic
              
              
                BioPhoton 83acdd7
              
                feat(testing-utils): add test helper for paths
              
              
                BioPhoton 7a7afcd
              
                test(nx-plugin): add tests
              
              
                BioPhoton 86a3c0e
              
                fix(nx-plugin): fix lint
              
              
                BioPhoton ac147ca
              
                fix(nx-plugin): fix test for windows
              
              
                BioPhoton 5186c69
              
                test(nx-plugin): use test helper for paths
              
              
                BioPhoton 330e9d6
              
                test(nx-plugin): use test helper for paths 2
              
              
                BioPhoton 2e15bd6
              
                test(nx-plugin): use test helper for paths 3
              
              
                BioPhoton e68f754
              
                test(nx-plugin): adjust test
              
              
                BioPhoton 2d1796f
              
                test(nx-plugin): add tests
              
              
                BioPhoton 95dfbcf
              
                test(nx-plugin): add tests 2
              
              
                BioPhoton 4f2f4f9
              
                refactor(nx-plugins): adjust tests and logic
              
              
                BioPhoton 6d79171
              
                refactor(nx-plugins): adjust tests
              
              
                BioPhoton 44deae9
              
                Update packages/nx-plugin/src/generators/configuration/generator.ts
              
              
                BioPhoton e802585
              
                Update packages/nx-plugin/src/generators/configuration/schema.json
              
              
                BioPhoton 68ce2fa
              
                refactor(nx-plugins): adjust docs
              
              
                BioPhoton 2ad2e93
              
                refactor(nx-plugins): reduce logic
              
              
                BioPhoton 1972ba8
              
                refactor(nx-plugins): cleanup
              
              
                BioPhoton 5573e7a
              
                refactor(nx-plugins): format
              
              
                BioPhoton 0fda1da
              
                Update packages/nx-plugin/src/executors/internal/env.unit.test.ts
              
              
                BioPhoton fc411fa
              
                refactor(nx-plugins): adjust outputDir
              
              
                BioPhoton File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export const ENV = { | ||
| CP_SERVER: 'https://portal.code.pushup.dev', | ||
| CP_ORGANIZATION: 'code-pushup', | ||
| CP_PROJECT: 'utils', | ||
| CP_API_KEY: '23456789098765432345678909876543', | ||
| CP_TIMEOUT: 9, | ||
| }; | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| export function createCliCommand( | ||
| command: string, | ||
| args: Record<string, unknown>, | ||
| ): string { | ||
| return `npx @code-pushup/cli ${command} ${objectToCliArgs(args).join(' ')}`; | ||
| } | ||
|  | ||
| type ArgumentValue = number | string | boolean | string[]; | ||
| export type CliArgsObject<T extends object = Record<string, ArgumentValue>> = | ||
| T extends never | ||
| ? // eslint-disable-next-line @typescript-eslint/naming-convention | ||
| Record<string, ArgumentValue | undefined> | { _: string } | ||
| : T; | ||
| // @TODO import from @code-pushup/utils => get rid of poppins for cjs support | ||
| // eslint-disable-next-line sonarjs/cognitive-complexity | ||
| export function objectToCliArgs< | ||
| T extends object = Record<string, ArgumentValue>, | ||
| >(params?: CliArgsObject<T>): string[] { | ||
| if (!params) { | ||
| return []; | ||
| } | ||
|  | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
| return Object.entries(params).flatMap(([key, value]) => { | ||
| // process/file/script | ||
| if (key === '_') { | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
| return Array.isArray(value) ? value : [`${value}`]; | ||
| } | ||
| const prefix = key.length === 1 ? '-' : '--'; | ||
| // "-*" arguments (shorthands) | ||
| if (Array.isArray(value)) { | ||
| return value.map(v => `${prefix}${key}="${v}"`); | ||
| } | ||
|  | ||
| if (typeof value === 'object') { | ||
| return Object.entries(value as Record<string, unknown>).map( | ||
| ([k, v]) => `${prefix}${key}.${k}="${v?.toString()}"`, | ||
| ); | ||
| } | ||
|  | ||
| if (typeof value === 'string') { | ||
| return [`${prefix}${key}="${value}"`]; | ||
| } | ||
|  | ||
| if (typeof value === 'number') { | ||
| return [`${prefix}${key}=${value}`]; | ||
| } | ||
|  | ||
| if (typeof value === 'boolean') { | ||
| return [`${prefix}${value ? '' : 'no-'}${key}`]; | ||
| } | ||
|  | ||
| if (value === undefined) { | ||
| return []; | ||
| } | ||
|  | ||
| throw new Error(`Unsupported type ${typeof value} for key ${key}`); | ||
| }); | ||
| } | 
        
          
  
    
      
          
            84 changes: 84 additions & 0 deletions
          
          84 
        
  packages/nx-plugin/src/executors/internal/cli.unit.test.ts
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import { describe, expect, it } from 'vitest'; | ||
| import { createCliCommand, objectToCliArgs } from './cli'; | ||
|  | ||
| describe('objectToCliArgs', () => { | ||
| it('should empty params', () => { | ||
| const result = objectToCliArgs(); | ||
| expect(result).toEqual([]); | ||
| }); | ||
|  | ||
| it('should handle the "_" argument as script', () => { | ||
| const params = { _: 'bin.js' }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['bin.js']); | ||
| }); | ||
|  | ||
| it('should handle the "_" argument with multiple values', () => { | ||
| const params = { _: ['bin.js', '--help'] }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['bin.js', '--help']); | ||
| }); | ||
|  | ||
| it('should handle shorthands arguments', () => { | ||
| const params = { | ||
| e: `test`, | ||
| }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual([`-e="${params.e}"`]); | ||
| }); | ||
|  | ||
| it('should handle string arguments', () => { | ||
| const params = { name: 'Juanita' }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['--name="Juanita"']); | ||
| }); | ||
|  | ||
| it('should handle number arguments', () => { | ||
| const params = { parallel: 5 }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['--parallel=5']); | ||
| }); | ||
|  | ||
| it('should handle boolean arguments', () => { | ||
| const params = { progress: true }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['--progress']); | ||
| }); | ||
|  | ||
| it('should handle negated boolean arguments', () => { | ||
| const params = { progress: false }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['--no-progress']); | ||
| }); | ||
|  | ||
| it('should handle array of string arguments', () => { | ||
| const params = { format: ['json', 'md'] }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toEqual(['--format="json"', '--format="md"']); | ||
| }); | ||
|  | ||
| it('should handle objects', () => { | ||
| const params = { format: { json: 'simple' } }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toStrictEqual(['--format.json="simple"']); | ||
| }); | ||
|  | ||
| it('should handle objects with undefined', () => { | ||
| const params = { format: undefined }; | ||
| const result = objectToCliArgs(params); | ||
| expect(result).toStrictEqual([]); | ||
| }); | ||
|  | ||
| it('should throw error for unsupported type', () => { | ||
| expect(() => objectToCliArgs({ param: Symbol('') })).toThrow( | ||
| 'Unsupported type', | ||
| ); | ||
| }); | ||
| }); | ||
|  | ||
| describe('createCliCommand', () => { | ||
| it('should create command out of command name and an object for arguments', () => { | ||
| const result = createCliCommand('autorun', { verbose: true }); | ||
| expect(result).toBe('npx @code-pushup/cli autorun --verbose'); | ||
| }); | ||
| }); | 
        
          
  
    
      
          
            49 changes: 49 additions & 0 deletions
          
          49 
        
  packages/nx-plugin/src/executors/internal/config.integration.test.ts
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { describe, expect } from 'vitest'; | ||
| import { ENV } from '../../../mock/fixtures/env'; | ||
| import { uploadConfig } from './config'; | ||
| import { parseEnv } from './env'; | ||
|  | ||
| vi.mock('./env', async () => { | ||
| const actual = await vi.importActual('./env'); | ||
| return { | ||
| ...actual, | ||
| parseEnv: vi.fn(actual.parseEnv as typeof parseEnv), | ||
| }; | ||
| }); | ||
|  | ||
| describe('uploadConfig', () => { | ||
| it('should call parseEnv function with values from process.env', () => { | ||
| const old = process.env; | ||
|  | ||
| // eslint-disable-next-line functional/immutable-data | ||
| process.env = ENV; | ||
|  | ||
| expect( | ||
| uploadConfig( | ||
| { | ||
| server: 'https://portal.code.pushup.dev', | ||
| }, | ||
| { | ||
| workspaceRoot: 'workspaceRoot', | ||
| projectConfig: { | ||
| name: 'my-app', | ||
| root: 'root', | ||
| }, | ||
| }, | ||
| ), | ||
| ).toEqual( | ||
| expect.objectContaining({ | ||
| server: ENV.CP_SERVER, | ||
| apiKey: ENV.CP_API_KEY, | ||
| organization: ENV.CP_ORGANIZATION, | ||
| project: ENV.CP_PROJECT, | ||
| }), | ||
| ); | ||
|  | ||
| expect(parseEnv).toHaveBeenCalledTimes(1); | ||
| expect(parseEnv).toHaveBeenCalledWith(ENV); | ||
|  | ||
| // eslint-disable-next-line functional/immutable-data | ||
| process.env = old; | ||
| }); | ||
| }); | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { join } from 'node:path'; | ||
| import type { PersistConfig, UploadConfig } from '@code-pushup/models'; | ||
| import { slugify } from '../../internal/utils'; | ||
| import { parseEnv } from './env'; | ||
| import { | ||
| BaseNormalizedExecutorContext, | ||
| GlobalExecutorOptions, | ||
| ProjectExecutorOnlyOptions, | ||
| } from './types'; | ||
|  | ||
| export function globalConfig( | ||
| options: Partial<GlobalExecutorOptions>, | ||
| context: BaseNormalizedExecutorContext, | ||
| ): Required<GlobalExecutorOptions> { | ||
| const { projectConfig } = context; | ||
| const { root: projectRoot = '' } = projectConfig ?? {}; | ||
| // For better debugging use `--verbose --no-progress` as default | ||
| const { verbose, progress, config } = options; | ||
| return { | ||
| verbose: !!verbose, | ||
| progress: !!progress, | ||
| config: config ?? join(projectRoot, 'code-pushup.config.json'), | ||
|         
                  BioPhoton marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| }; | ||
| } | ||
|  | ||
| export function persistConfig( | ||
| options: Partial<PersistConfig & ProjectExecutorOnlyOptions>, | ||
| context: BaseNormalizedExecutorContext, | ||
| ): Partial<PersistConfig> { | ||
| const { projectConfig } = context; | ||
|  | ||
| const { name: projectName = '', root: projectRoot = '' } = | ||
| projectConfig ?? {}; | ||
| const { | ||
| format = ['json'], | ||
|         
                  BioPhoton marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| outputDir = join(projectRoot, '.code-pushup', projectName), // always in <root>/.code-pushup/<project-name>, | ||
|         
                  BioPhoton marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| filename: filenameOptions, | ||
| } = options; | ||
|  | ||
| return { | ||
| format, | ||
| outputDir, | ||
| ...(filenameOptions ? { filename: slugify(filenameOptions) } : {}), | ||
|         
                  BioPhoton marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| }; | ||
| } | ||
|  | ||
| export function uploadConfig( | ||
| options: Partial<UploadConfig & ProjectExecutorOnlyOptions>, | ||
| context: BaseNormalizedExecutorContext, | ||
| ): Partial<UploadConfig> { | ||
| const { projectConfig, workspaceRoot } = context; | ||
|  | ||
| const { name: projectName } = projectConfig ?? {}; | ||
| const { projectPrefix, server, apiKey, organization, project, timeout } = | ||
| options; | ||
| const applyPrefix = workspaceRoot === '.'; | ||
| const prefix = projectPrefix ? `${projectPrefix}-` : ''; | ||
| return { | ||
| ...(projectName | ||
| ? { | ||
| project: applyPrefix ? `${prefix}${projectName}` : projectName, // provide correct project | ||
| } | ||
| : {}), | ||
| ...parseEnv(process.env), | ||
| ...Object.fromEntries( | ||
| Object.entries({ server, apiKey, organization, project, timeout }).filter( | ||
| ([_, v]) => v !== undefined, | ||
| ), | ||
| ), | ||
| }; | ||
| } | ||
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.