Skip to content

feat(themes): add themes:export command#383

Open
luis-almeida wants to merge 2 commits into
masterfrom
luis/themes-export
Open

feat(themes): add themes:export command#383
luis-almeida wants to merge 2 commits into
masterfrom
luis/themes-export

Conversation

@luis-almeida

@luis-almeida luis-almeida commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

zcli-themes wraps the Help Center theming endpoints (import/update/publish/delete/list) but had no way to pull a theme back down. This adds zcli 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.

de0cda3 feat(themes): add themes:export command

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_.zip.

8ca6093 fix(themes): create export destination dir and harden failure tests

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.

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.

$ zcli themes:export --themeId=123456789100
$ zcli themes:export ./exports --themeId=123456789100

The theme is written to the optional [THEMEDIRECTORY] arg (default: cwd) as theme_<themeId>.zip. themeId is prompted for when the flag is omitted, mirroring themes:publish/themes:delete.

Checklist

  • 💂‍♂️ includes new unit and functional tests

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.
@luis-almeida
luis-almeida marked this pull request as ready for review July 8, 2026 15:41
@luis-almeida
luis-almeida requested a review from a team as a code owner July 8, 2026 15:41
Copilot AI review requested due to automatic review settings July 8, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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:export oclif 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.

Comment thread packages/zcli-themes/src/commands/themes/export.ts
Comment thread packages/zcli-themes/src/lib/downloadThemePackage.ts
Comment thread packages/zcli-themes/src/lib/downloadThemePackage.ts
Comment thread packages/zcli-themes/src/lib/downloadThemePackage.ts
Comment thread packages/zcli-themes/src/lib/downloadThemePackage.ts
Comment thread packages/zcli-themes/src/lib/downloadThemePackage.test.ts
Comment thread packages/zcli-themes/src/lib/createThemeExportJob.test.ts
Comment thread packages/zcli-themes/tests/functional/export.test.ts
Comment thread packages/zcli-themes/tests/functional/export.test.ts
Comment thread packages/zcli-themes/tests/functional/export.test.ts
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.
Copilot AI review requested due to automatic review settings July 10, 2026 13:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.

Comment on lines +17 to +19
static args = [
{ name: 'themeDirectory', required: true, default: '.' }
]
Comment on lines +29 to +30
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'
Comment on lines +84 to +87
.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`)
Comment on lines +21 to +22
fs.mkdirSync(destination, { recursive: true })
fs.writeFileSync(filePath, Buffer.from(response.data))
Comment on lines +21 to +22
fs.mkdirSync(destination, { recursive: true })
fs.writeFileSync(filePath, Buffer.from(response.data))
Comment on lines +28 to +30
} catch (error) {
handleThemeApiError(error as AxiosError)
}

@melzreal melzreal left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tried it and works a charm 👍

// waits for the export to finish (the completed job's `data` is null).
const downloadUrl = job.data.download.url

await pollJobStatus(destination, job.id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: pollJobStatus says it receives themePath as an argument so probably would make sense to call it that instead of destination

@BrunoBFerreira BrunoBFerreira left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Left two comments but overall looks really good.
Thank you for addressing this 🙏


beforeEach(() => {
fetchStub = sinon.stub(global, 'fetch')
writeFileStub = sinon.stub(fs, 'writeFileSync')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we also stub mkdirSync?

}
})
})
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we also add a test with the --themeId missing? I believe that path is not covered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants