feat(themes): add themes:export command#383
Conversation
Add zcli themes:export to create a Help Center theme export job, poll it to completion, and download the resulting zip to a target directory (default: cwd) as theme_<themeId>.zip.
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds a zcli themes:export command to export a Help Center theme by creating an async export job, polling until completion, and downloading the resulting zip for local storage (enabling backups and round-trips).
Changes:
- Introduces
themes:exportoclif command (create export job → poll → download zip). - Adds supporting lib helpers for job creation and package download.
- Adds functional/unit tests and CLI docs for the new command.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/zcli-themes/tests/functional/export.test.ts | Functional coverage for themes:export success and failure paths. |
| packages/zcli-themes/src/types.ts | Adds ExportJob typing for export-job creation response. |
| packages/zcli-themes/src/lib/downloadThemePackage.ts | Downloads zip from presigned URL and writes it to disk. |
| packages/zcli-themes/src/lib/downloadThemePackage.test.ts | Unit tests for download/write and error behavior. |
| packages/zcli-themes/src/lib/createThemeExportJob.ts | Creates the export job via theming jobs API. |
| packages/zcli-themes/src/lib/createThemeExportJob.test.ts | Unit tests validating payload and error handling. |
| packages/zcli-themes/src/commands/themes/export.ts | New themes:export command implementation. |
| docs/themes.md | Documents the new themes:export command usage/options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address PR review: themes:export now creates the destination directory (recursive) before writing, so exporting to a not-yet-existing path no longer fails with ENOENT. Failure-path tests now use a sentinel throw so they can't silently pass when no error is raised.
| static args = [ | ||
| { name: 'themeDirectory', required: true, default: '.' } | ||
| ] |
| let { flags: { themeId }, argv: [themeDirectory] } = await this.parse(Export) | ||
| const destination = path.resolve(themeDirectory) |
| @@ -0,0 +1,188 @@ | |||
| import type { ExportJob } from '../../../zcli-themes/src/types' | |||
| .it('should display success message when the theme is exported successfully', async ctx => { | ||
| await ExportCommand.run(['--themeId', '1234']) | ||
| expect(ctx.stdout).to.contain('Theme exported successfully theme ID: 1234') | ||
| }) |
| export default async function downloadThemePackage (downloadUrl: string, themeId: string, destination: string): Promise<string> { | ||
| CliUx.ux.action.start('Downloading theme package') | ||
|
|
||
| const filePath = path.join(destination, `theme_${themeId}.zip`) |
| fs.mkdirSync(destination, { recursive: true }) | ||
| fs.writeFileSync(filePath, Buffer.from(response.data)) |
| fs.mkdirSync(destination, { recursive: true }) | ||
| fs.writeFileSync(filePath, Buffer.from(response.data)) |
| } catch (error) { | ||
| handleThemeApiError(error as AxiosError) | ||
| } |
| // waits for the export to finish (the completed job's `data` is null). | ||
| const downloadUrl = job.data.download.url | ||
|
|
||
| await pollJobStatus(destination, job.id) |
There was a problem hiding this comment.
nit: pollJobStatus says it receives themePath as an argument so probably would make sense to call it that instead of destination
BrunoBFerreira
left a comment
There was a problem hiding this comment.
Left two comments but overall looks really good.
Thank you for addressing this 🙏
|
|
||
| beforeEach(() => { | ||
| fetchStub = sinon.stub(global, 'fetch') | ||
| writeFileStub = sinon.stub(fs, 'writeFileSync') |
There was a problem hiding this comment.
Should we also stub mkdirSync?
| } | ||
| }) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Should we also add a test with the --themeId missing? I believe that path is not covered.
Description
zcli-themeswraps the Help Center theming endpoints (import/update/publish/delete/list) but had no way to pull a theme back down. This addszcli themes:export, closing that gap so users can retrieve a theme as a zip — enabling round-trips (export → edit → re-import) and backups without leaving the CLI.de0cda3feat(themes): add themes:export commandAdd zcli themes:export to create a Help Center theme export job, poll it
to completion, and download the resulting zip to a target directory
(default: cwd) as theme_.zip.
8ca6093fix(themes): create export destination dir and harden failure testsAddress PR review: themes:export now creates the destination directory
(recursive) before writing, so exporting to a not-yet-existing path no
longer fails with ENOENT. Failure-path tests now use a sentinel throw so
they can't silently pass when no error is raised.
Detail
Wraps the async theme export job API: create job (
POST .../jobs/themes/exports) → poll to completion → download the zip from the presigned URL in the created job.The theme is written to the optional
[THEMEDIRECTORY]arg (default: cwd) astheme_<themeId>.zip.themeIdis prompted for when the flag is omitted, mirroringthemes:publish/themes:delete.Checklist