-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(nx-python): add poetry publish executor (#241)
- Loading branch information
1 parent
0db96e5
commit f34b797
Showing
10 changed files
with
1,384 additions
and
146 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains 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 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 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 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 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
204 changes: 204 additions & 0 deletions
204
packages/nx-python/src/executors/publish/executor.spec.ts
This file contains 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,204 @@ | ||
import { vi, MockInstance } from 'vitest'; | ||
|
||
const fsExtraMocks = vi.hoisted(() => { | ||
return { | ||
removeSync: vi.fn(), | ||
}; | ||
}); | ||
|
||
const nxDevkitMocks = vi.hoisted(() => { | ||
return { | ||
runExecutor: vi.fn(), | ||
}; | ||
}); | ||
|
||
vi.mock('@nx/devkit', async (importOriginal) => { | ||
const actual = await importOriginal<typeof import('@nx/devkit')>(); | ||
return { | ||
...actual, | ||
...nxDevkitMocks, | ||
}; | ||
}); | ||
|
||
vi.mock('fs-extra', async (importOriginal) => { | ||
const actual = await importOriginal<typeof import('fs-extra')>(); | ||
return { | ||
...actual, | ||
...fsExtraMocks, | ||
}; | ||
}); | ||
|
||
import chalk from 'chalk'; | ||
import '../../utils/mocks/cross-spawn.mock'; | ||
import * as poetryUtils from '../utils/poetry'; | ||
import executor from './executor'; | ||
import spawn from 'cross-spawn'; | ||
|
||
describe('Publish Executor', () => { | ||
let checkPoetryExecutableMock: MockInstance; | ||
let activateVenvMock: MockInstance; | ||
|
||
const context = { | ||
cwd: '', | ||
root: '.', | ||
isVerbose: false, | ||
projectName: 'app', | ||
workspace: { | ||
version: 2, | ||
projects: { | ||
app: { | ||
root: 'apps/app', | ||
targets: {}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
beforeEach(() => { | ||
checkPoetryExecutableMock = vi | ||
.spyOn(poetryUtils, 'checkPoetryExecutable') | ||
.mockResolvedValue(undefined); | ||
|
||
activateVenvMock = vi | ||
.spyOn(poetryUtils, 'activateVenv') | ||
.mockReturnValue(undefined); | ||
|
||
vi.mocked(spawn.sync).mockReturnValue({ | ||
status: 0, | ||
output: [''], | ||
pid: 0, | ||
signal: null, | ||
stderr: null, | ||
stdout: null, | ||
}); | ||
|
||
vi.spyOn(process, 'chdir').mockReturnValue(undefined); | ||
}); | ||
|
||
beforeAll(() => { | ||
console.log(chalk`init chalk`); | ||
}); | ||
|
||
afterEach(() => { | ||
vi.resetAllMocks(); | ||
}); | ||
|
||
it('should return success false when the poetry is not installed', async () => { | ||
checkPoetryExecutableMock.mockRejectedValue(new Error('poetry not found')); | ||
|
||
const options = { | ||
buildTarget: 'build', | ||
silent: false, | ||
}; | ||
|
||
const output = await executor(options, context); | ||
expect(checkPoetryExecutableMock).toHaveBeenCalled(); | ||
expect(activateVenvMock).toHaveBeenCalledWith('.'); | ||
expect(spawn.sync).not.toHaveBeenCalled(); | ||
expect(output.success).toBe(false); | ||
}); | ||
|
||
it('should return success false when the build target fails', async () => { | ||
nxDevkitMocks.runExecutor.mockResolvedValueOnce([{ success: false }]); | ||
|
||
const options = { | ||
buildTarget: 'build', | ||
silent: false, | ||
}; | ||
|
||
const output = await executor(options, context); | ||
expect(checkPoetryExecutableMock).toHaveBeenCalled(); | ||
expect(activateVenvMock).toHaveBeenCalledWith('.'); | ||
expect(spawn.sync).not.toHaveBeenCalled(); | ||
expect(output.success).toBe(false); | ||
}); | ||
|
||
it('should return success false when the build target does not return the temp folder', async () => { | ||
nxDevkitMocks.runExecutor.mockResolvedValueOnce([{ success: true }]); | ||
|
||
const options = { | ||
buildTarget: 'build', | ||
silent: false, | ||
__unparsed__: [], | ||
}; | ||
|
||
const output = await executor(options, context); | ||
expect(checkPoetryExecutableMock).toHaveBeenCalled(); | ||
expect(activateVenvMock).toHaveBeenCalledWith('.'); | ||
expect(spawn.sync).not.toHaveBeenCalled(); | ||
expect(output.success).toBe(false); | ||
}); | ||
|
||
it('should run poetry publish command without agrs', async () => { | ||
nxDevkitMocks.runExecutor.mockResolvedValueOnce([ | ||
{ success: true, buildFolderPath: 'tmp' }, | ||
]); | ||
fsExtraMocks.removeSync.mockReturnValue(undefined); | ||
|
||
const options = { | ||
buildTarget: 'build', | ||
silent: false, | ||
}; | ||
|
||
const output = await executor(options, context); | ||
expect(checkPoetryExecutableMock).toHaveBeenCalled(); | ||
expect(activateVenvMock).toHaveBeenCalledWith('.'); | ||
expect(spawn.sync).toHaveBeenCalledWith('poetry', ['publish'], { | ||
cwd: 'tmp', | ||
shell: false, | ||
stdio: 'inherit', | ||
}); | ||
expect(output.success).toBe(true); | ||
expect(nxDevkitMocks.runExecutor).toHaveBeenCalledWith( | ||
{ | ||
configuration: undefined, | ||
project: 'app', | ||
target: 'build', | ||
}, | ||
{ | ||
keepBuildFolder: true, | ||
}, | ||
context, | ||
); | ||
expect(fsExtraMocks.removeSync).toHaveBeenCalledWith('tmp'); | ||
}); | ||
|
||
it('should run poetry publish command with agrs', async () => { | ||
nxDevkitMocks.runExecutor.mockResolvedValueOnce([ | ||
{ success: true, buildFolderPath: 'tmp' }, | ||
]); | ||
fsExtraMocks.removeSync.mockReturnValue(undefined); | ||
|
||
const options = { | ||
buildTarget: 'build', | ||
silent: false, | ||
__unparsed__: ['-vvv', '--dry-run'], | ||
}; | ||
|
||
const output = await executor(options, context); | ||
expect(checkPoetryExecutableMock).toHaveBeenCalled(); | ||
expect(activateVenvMock).toHaveBeenCalledWith('.'); | ||
expect(spawn.sync).toHaveBeenCalledWith( | ||
'poetry', | ||
['publish', '-vvv', '--dry-run'], | ||
{ | ||
cwd: 'tmp', | ||
shell: false, | ||
stdio: 'inherit', | ||
}, | ||
); | ||
expect(output.success).toBe(true); | ||
expect(nxDevkitMocks.runExecutor).toHaveBeenCalledWith( | ||
{ | ||
configuration: undefined, | ||
project: 'app', | ||
target: 'build', | ||
}, | ||
{ | ||
keepBuildFolder: true, | ||
}, | ||
context, | ||
); | ||
expect(fsExtraMocks.removeSync).toHaveBeenCalledWith('tmp'); | ||
}); | ||
}); |
This file contains 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,69 @@ | ||
import { ExecutorContext, runExecutor } from '@nx/devkit'; | ||
import { PublishExecutorSchema } from './schema'; | ||
import chalk from 'chalk'; | ||
import { Logger } from '../utils/logger'; | ||
import { | ||
activateVenv, | ||
checkPoetryExecutable, | ||
runPoetry, | ||
} from '../utils/poetry'; | ||
import { BuildExecutorOutput } from '../build/schema'; | ||
import { removeSync } from 'fs-extra'; | ||
|
||
const logger = new Logger(); | ||
|
||
export default async function executor( | ||
options: PublishExecutorSchema, | ||
context: ExecutorContext, | ||
) { | ||
logger.setOptions(options); | ||
const workspaceRoot = context.root; | ||
process.chdir(workspaceRoot); | ||
try { | ||
activateVenv(workspaceRoot); | ||
await checkPoetryExecutable(); | ||
|
||
let buildFolderPath = ''; | ||
|
||
for await (const output of await runExecutor<BuildExecutorOutput>( | ||
{ | ||
project: context.projectName, | ||
target: options.buildTarget, | ||
configuration: context.configurationName, | ||
}, | ||
{ | ||
keepBuildFolder: true, | ||
}, | ||
context, | ||
)) { | ||
if (!output.success) { | ||
throw new Error('Build failed'); | ||
} | ||
|
||
buildFolderPath = output.buildFolderPath; | ||
} | ||
|
||
if (!buildFolderPath) { | ||
throw new Error('Cannot find the temporary build folder'); | ||
} | ||
|
||
logger.info( | ||
chalk`\n {bold Publishing project {bgBlue ${context.projectName} }...}\n`, | ||
); | ||
|
||
await runPoetry(['publish', ...(options.__unparsed__ ?? [])], { | ||
cwd: buildFolderPath, | ||
}); | ||
|
||
removeSync(buildFolderPath); | ||
|
||
return { | ||
success: true, | ||
}; | ||
} catch (error) { | ||
logger.info(chalk`\n {bgRed.bold ERROR } ${error.message}\n`); | ||
return { | ||
success: false, | ||
}; | ||
} | ||
} |
This file contains 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,5 @@ | ||
export interface PublishExecutorSchema { | ||
silent: boolean; | ||
buildTarget: string; | ||
__unparsed__?: string[]; | ||
} |
Oops, something went wrong.