Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
0413c47
feat: add support for campaign pages, and enhance deployment options
xolott-ark Apr 17, 2026
b2e88c0
chore: bump version to 2.0.0-alpha.1
xolott-ark Apr 17, 2026
fab7a78
feat: refactored login and logout functionalities to support OAuth 2.…
xolott-ark Apr 20, 2026
6adb7c9
feat: add 'raisely list' command to display all campaigns with option…
xolott-ark Apr 20, 2026
b3c49e8
feat: add option to initialize a Raisely campaign directly by UUID, e…
xolott-ark Apr 20, 2026
f14e2d5
feat: enhance 'raisely local' command to open specific campaigns by U…
xolott-ark Apr 20, 2026
0223d98
feat: add support for proxy URL configuration from environment variab…
xolott-ark Apr 20, 2026
a71d46e
feat: implement OAuth login flow for credential retrieval and add API…
xolott-ark Apr 20, 2026
a8912c2
refactor: remove node-sass dependency and integrate SASS transpilatio…
xolott-ark Apr 22, 2026
f970c5a
fix: update OAuth scopes to include 'pages:update' for improved permi…
xolott-ark Apr 22, 2026
4deb57c
chore: add '--force' option for non-interactive updates in CLI
xolott-ark Apr 23, 2026
4c96e73
chore: update OAuth client ID placeholder in credentials file
xolott-ark Apr 23, 2026
a405aa8
feat: add '--no-open' option to CLI and modify local command behavior…
xolott-ark Apr 24, 2026
d98a840
feat(SIR-1516): test infra and path resolver (src/actions/layout.js)
KeinerM Apr 29, 2026
693a0e8
test: add special campaign path coverage for layout detection
KeinerM Apr 29, 2026
aca08bb
Merge pull request #61 from raisely/keinermartinez/sir-1516-cli-v2-te…
KeinerM Apr 30, 2026
a6a7b69
chore: ignore .pnpm-store
KeinerM May 1, 2026
c3b157e
feat(cli): add raisely migrate command (SIR-1517)
KeinerM May 1, 2026
bc9ddd0
fix(migrate): classify moved-not-renamed files correctly in report (S…
KeinerM May 1, 2026
9d04ef6
feat(SIR-1519): switch readers and writers to v2 layout
KeinerM May 1, 2026
2dd1610
test(SIR-1519): fixture tests for v2 reads, single and multi-campaign
KeinerM May 1, 2026
4503f71
Merge pull request #62 from raisely/keinermartinez/sir-1517-cli-v2-ra…
KeinerM May 2, 2026
6907dfb
Merge pull request #63 from raisely/keinermartinez/sir-1519-cli-v2-sw…
KeinerM May 2, 2026
aace633
feat(local): cache last good css on transpiler failures (SIR-1520)
KeinerM May 4, 2026
c6e0942
test(local): cover css fallback, 401, and 5xx retry (SIR-1520)
KeinerM May 4, 2026
856f1de
refactor(local): unify transpiler retry flow (SIR-1520)
KeinerM May 4, 2026
b625ff0
test(local): extend retry and logging assertions (SIR-1520)
KeinerM May 4, 2026
b1efc80
refactor(local): share sleep helper and clarify retry invariant (SIR-…
KeinerM May 4, 2026
bb4a684
feat(cli): add legacy layout refusal gate (SIR-1518)
KeinerM May 4, 2026
555d4cd
test(cli): cover legacy and v2 layout guards (SIR-1518)
KeinerM May 4, 2026
8e31db1
test(local): harden error log assertions for mixed entry types (SIR-1…
KeinerM May 4, 2026
1826dbb
fix(local): prevent stale requests from overwriting css cache (SIR-1520)
KeinerM May 4, 2026
d9cd0e6
test(local): cover concurrent cache update ordering (SIR-1520)
KeinerM May 4, 2026
36556b9
Merge pull request #65 from raisely/keinermartinez/sir-1518-cli-v2-le…
KeinerM May 5, 2026
777fe42
feat(local): allows to set port
xolott-ark May 5, 2026
03c5154
Merge pull request #66 from raisely/jose/sir-1538-override-default-port
xolott-ark May 5, 2026
9e145f7
feat(validate): add shared scss and component validators (SIR-1521)
KeinerM May 5, 2026
f4d3caa
test(validate): cover transpiler auth retry and babel failures (SIR-1…
KeinerM May 5, 2026
3021c78
refactor(validate): share Babel loader across actions (SIR-1521)
KeinerM May 5, 2026
67c1682
fix(validate): normalize transpiler URL and guard missing token (SIR-…
KeinerM May 5, 2026
e552d10
Merge pull request #64 from raisely/keinermartinez/sir-1520-cli-v2-la…
KeinerM May 5, 2026
300e7df
Merge pull request #67 from raisely/keinermartinez/sir-1521-cli-v2-sh…
KeinerM May 5, 2026
16033b7
feat(deploy): add pre-flight validation gate for deploy (SIR-1522)
KeinerM May 6, 2026
a2370fb
test(deploy): cover validation gate failure modes (SIR-1522)
KeinerM May 6, 2026
46b84a8
test(cli): migrate test suite from node:test to vitest (SIR-1522)
KeinerM May 6, 2026
bdc4e41
fix(deploy): harden validator handling and spinner cleanup (SIR-1522)
KeinerM May 6, 2026
4b95fa8
fix(deploy): bound validation concurrency and guard components dir (S…
KeinerM May 6, 2026
1ffc4d6
refactor(deploy): remove DI and mock modules in tests (SIR-1522)
KeinerM May 7, 2026
4aa369b
fix(deploy): harden preflight errors and drop DI in tests (SIR-1522)
KeinerM May 7, 2026
6994648
chore(deps): pin Node 18-compatible latest versions (SIR-1522)
KeinerM May 7, 2026
4b76e0c
chore(deps): align Babel compiler versions for CLI compatibility (SIR…
KeinerM May 7, 2026
c11b854
Merge pull request #68 from raisely/sir-1522-cli-v2-pre-flight-valida…
KeinerM May 8, 2026
4b69dea
feat(start): validate watched saves before upload (SIR-1523)
KeinerM May 6, 2026
a451eb9
test(start): migrate start tests to vitest (SIR-1523)
KeinerM May 7, 2026
fdfebd9
Merge pull request #69 from raisely/sir-1523-cli-v2-per-save-validati…
KeinerM May 8, 2026
8f77176
docs(cli): update v2 release docs and layout examples (SIR-1524)
KeinerM May 11, 2026
f30e335
Merge pull request #70 from raisely/sir-1524-cli-v2-release-docs-chan…
KeinerM May 11, 2026
50af153
feat(cli): track command telemetry events (SIR-1556)
KeinerM May 13, 2026
a9776ca
fix(cli): add telemetry request timeouts (SIR-1556)
KeinerM May 13, 2026
75396e2
fix(cli): avoid dropping telemetry during flush (SIR-1556)
KeinerM May 13, 2026
c14dfc3
fix(cli): rename client identification header (SIR-1556)
KeinerM May 14, 2026
73b34cb
Merge pull request #71 from raisely/keinermartinez/sir-1556-add-telem…
KeinerM May 15, 2026
617164c
fix(cli): use descriptive telemetry payload fields (SIR-1556)
KeinerM May 15, 2026
f9d8be4
fix(cli): use jwt-decode named export in logout (SIR-1556)
KeinerM May 15, 2026
30fd667
Merge pull request #72 from raisely/keinermartinez/sir-1556-improve-t…
KeinerM May 15, 2026
493bc04
chore: bump version to 2.0.0-alpha.3 in package.json
xolott-ark May 21, 2026
76e24ad
Merge pull request #73 from raisely/josetruyol/bump-alpha-version
xolott-ark May 25, 2026
b1b1d30
feat(cli): add `raisely media list` and `raisely media delete` (SIR-1…
KeinerM Jun 18, 2026
650c8cb
fix(cli): harden api.js and add upload actions for media (SIR-1656)
KeinerM Jun 19, 2026
3d020dd
feat(cli): add `raisely media upload` command (SIR-1656)
KeinerM Jun 19, 2026
0fc6b67
refactor(cli): use console.table for media list output
KeinerM Jun 19, 2026
0f72e1e
refactor(cli): replace manual multipart builder with form-data (SIR-1…
KeinerM Jun 19, 2026
f5415e7
Merge pull request #75 from raisely/keinermartinez/sir-1656-cli-raise…
KeinerM Jun 19, 2026
561e38f
Merge pull request #74 from raisely/keinermartinez/sir-1655-cli-raise…
KeinerM Jun 19, 2026
bb1fdf6
chore: bump version to 2.0.0 in package.json
xolott-ark Jul 6, 2026
2712e82
chore: update package-lock.json to version 2.0.0 and add new dependen…
xolott-ark Jul 6, 2026
b32f209
chore: remove package-lock.json as part of project cleanup
xolott-ark Jul 6, 2026
e709be4
chore: add pnpm workspace configuration for only built dependencies
xolott-ark Jul 6, 2026
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: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
node_modules
.pnpm-store
.DS_Store

# dev folders
stylesheets
components
.raisely.json
.idea

# allow test fixtures even when their folder names match the dev ignores above
!tests/
!tests/**
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@

# 2.0.0
- Breaking layout change: per-campaign content now lives in `campaigns/<campaign-path>/`, the entry SCSS file is now `main.scss`, and shared components remain at the repo root.
- Added `raisely migrate` to upgrade existing repositories from the v1 layout to the v2 layout.
- Added SCSS and component validation for `raisely deploy` (with `--no-validate`) and per-save validation for `raisely start`.
- Improved `raisely local` resilience by serving the last successful CSS when compilation fails, removing the 5-second retry loop, and removing process exit on transpiler 401 responses.
- Removed v1 compatibility read paths; v2 commands now require the v2 layout.

# 1.8.3
- Fix redirects for `raisely local`

Expand Down
87 changes: 75 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

![Raisely logo](https://raisely-images.imgix.net/www/uploads/lockup-default-svg-d0b31f.svg?fit=max&w=300&auto=format&q=62)

The Raisely CLI is used to power local development of Raisely themes, syncing custom components and campaign styles to your local machine.
The Raisely CLI is used to power local development of Raisely themes, syncing custom components, campaign styles, and campaign pages to your local machine.

For more about Raisely, see <https://raisely.com>
For more about Raisely, see [https://raisely.com](https://raisely.com)

## Overview

The Raisely CLI allows for fast and easy development on the Raisely platform. The CLI allows you to connect a directory on your local computer to a Raisely account. With the CLI you can update campaign stylesheets, and edit and create custom React components.
The Raisely CLI allows for fast and easy development on the Raisely platform. The CLI allows you to connect a directory on your local computer to a Raisely account. With the CLI you can update campaign stylesheets, edit and create custom React components, and edit page layout JSON under `campaigns/<campaign-path>/pages/`.

The CLI is built on Node.js, so you'll need Node.js installed to use it.

Expand All @@ -25,27 +25,90 @@ For other issues, [submit a support ticket](mailto:support@raisely.com).
1. Install the CLI globally: `npm install @raisely/cli -g`
2. Go into your working directory and run: `raisely init`

`raisely init` writes the v2 project layout:

```text
campaigns/
<campaign-path>/
pages/
home.json
stylesheets/
main.scss
components/
CustomHeader.js
```

## Commands

- `raisely init` - start a new Raisely project, authenticate and sync your campaigns
- `raisely update` - update local copies of styles and components from the API
- `raisely create [name]` - create a new custom component, optionally add the component name to the command (otherwise you will be asked for one)
- `raisely start` - starts watching for and uploading changes to styles and components
- `raisely deploy` - deploy your local code to Raisely
- `raisely local` - work locally on a Raisely campaign without syncing changes up
- `raisely init` - start a new Raisely project and sync your campaigns
- `raisely init --uuid <uuid>` - initialize directly against a specific campaign, skipping the picker
- `raisely list` - list every campaign (Name, Uuid), sorted A-Z; prints a padded table in a terminal, TSV when stdout is piped (`raisely list | cut -f2`), and `--json` / `--tsv` force a format
- `raisely login` - sign in with OAuth (opens your browser); stores access and refresh tokens in the OS keychain
- `raisely logout` - revoke the current access token when possible and clear keychain storage for this org
- `raisely update` - update local copies of styles, components, and pages from the API
- `raisely update --force` - same as above without the confirmation prompt (for CI/scripts)
- `raisely migrate` - migrate an existing repo from the legacy layout to the v2 layout
- `raisely create [name]` - create a new custom component, optionally add the component name to the command (otherwise you will be asked for one)
- `raisely start` - starts watching for and uploading validated changes to styles and components
- `raisely deploy` - deploy your local code to Raisely (styles, components, and pages)
- `raisely deploy --no-validate` - deploy without running pre-flight validation first
- `raisely local` - work locally on a Raisely campaign without syncing changes up (includes local page JSON overrides from `campaigns/<campaign-path>/pages/` when present)
- `raisely local --uuid <uuid>` - open a specific campaign by UUID, skipping the picker
- `raisely local --port <port>` - run the local development server on a custom port instead of `8015`
- `raisely media list` - list all media assets for a campaign or organisation; falls back to the first campaign in `.raisely.json` when no flag is passed
- `raisely media list --campaign <slug>` - list media for a specific campaign by path/slug
- `raisely media list --organisation <uuid>` - list media for an organisation by UUID
- `raisely media list --json` - output the media array as JSON instead of a table
- `raisely media delete <uuid>` - delete a media asset by UUID (no confirmation prompt)
- `raisely media upload <file-or-url>` - upload a local file or a public URL as a media asset; falls back to the first campaign in `.raisely.json` when no `--campaign` is passed
- `raisely media upload <file-or-url> --campaign <slug>` - upload to a specific campaign
- `raisely media upload <file-or-url> --organisation <uuid>` - upload to an organisation
- `raisely media upload <file-or-url> --force` - skip the confirmation prompt (required for non-interactive/agent use)
- `raisely media upload <file-or-url> --json` - skip the confirmation prompt and output the result as JSON

### Custom public host (`raisely local`)

By default, `raisely local` proxies to `https://{campaign.path}.raisely.com`. If the site people visit is on another host (for example `https://{campaign.path}.raiselysite.com`), add `**proxyUrl**` to `.raisely.json` with the bare domain (no campaign subdomain):

```json
{
"proxyUrl": "https://raiselysite.com"
}
```

The CLI turns that into `https://{campaign.path}.raiselysite.com` so the proxy matches production.

## Authentication

Interactive use relies on **OAuth 2.0 with PKCE** against `https://api.raisely.com/v1/oauth/authorize` and `/v1/oauth/token` (or your `RAISELY_API_URL` host for staging, for example `https://api.raisely.io`).

1. Register a **NATIVE** app in Raisely admin (Settings → Apps), add loopback redirect URIs such as `http://127.0.0.1:8765/callback` (and 8766, 8767), and note the app `client_id` (UUID).
2. Set `**RAISELY_OAUTH_CLIENT_ID`\*\* to that UUID before running `raisely login` or `raisely init`. The CLI ships with a placeholder `client_id` until you replace it in the package.
3. Optional: `**RAISELY_OAUTH_SCOPES**` overrides the default scopes (`campaigns:read campaigns:update pages:read`).

Tokens are stored in the OS keychain under service `@raisely/cli`, with account name `{api_host}:{organisation_uuid}` (for example `api.raisely.com:aaaaaaaa-...`). The file `~/.raisely/session.json` records the last successful login’s host and organisation so commands work before `.raisely.json` exists.

**Credential resolution order** for API calls:

1. `RAISELY_TOKEN` (personal access token / campaign key), if set
2. Keychain session for the current API host + `organisationUuid` (from `.raisely.json` or the session pointer)
3. Legacy `token` field in `.raisely.json`

## CI/CD Usage

Raisely CLI supports usage in a CI/CD environment for auto-deployment of styles and components. In this scenario you would use the CLI to deploy local code, and overwrite what is on a Raisely campaign or account.

Raisely CLI supports the following environment variables:

- `RAISELY_TOKEN` – your API secret key
- `RAISELY_CAMPAIGNS` - a comma-separated list of campaign uuids to sync (so you can be selective)
- `RAISELY_TOKEN` – your API secret key (overrides keychain and `.raisely.json` token)
- `RAISELY_CAMPAIGNS` - a comma-separated list of campaign uuids to sync (so you can be selective)
- `RAISELY_OAUTH_CLIENT_ID` - OAuth NATIVE app client id (required for `raisely login` until a built-in id ships)
- `RAISELY_OAUTH_SCOPES` - optional space-separated OAuth scopes
- `RAISELY_API_URL` - API base URL (default `https://api.raisely.com`)

_Note: All components are always synced, when they're present in the directory your syncing_

With these environment variables set, run: `raisely deploy`. This will sync your local directory to the remote Raisely account, overwriting the styles and components on the destination campaign.
With these environment variables set, run: `raisely deploy`. This will sync your local directory to the remote Raisely account, overwriting the styles, components, and pages on the destination campaign.

## Developing

Expand Down
53 changes: 29 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,45 +1,50 @@
{
"name": "@raisely/cli",
"version": "1.8.3",
"version": "2.0.0",
"description": "Raisely CLI for local development",
"main": "./src/cli.js",
"type": "module",
"bin": {
"raisely": "./bin/raisely.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "vitest run --config vitest.config.js"
},
"engines": {
"node": ">=18"
},
"author": "Raisely",
"license": "SEE LICENSE IN LICENSE.md",
"dependencies": {
"@babel/core": "^7.17.8",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"chalk": "^2.4.2",
"commander": "^9.2.0",
"express": "^4.17.3",
"folder-hash": "^4.0.2",
"fzstd": "^0.1.1",
"glob": "^7.1.6",
"glob-promise": "^3.4.0",
"http-proxy-middleware": "^2.0.4",
"inquirer": "^6.3.1",
"jwt-decode": "^3.0.0-beta.2",
"lodash": "^4.17.11",
"node-fetch": "^3.2.3",
"node-sass": "^9.0.0",
"node-watch": "^0.7.1",
"open": "^8.4.0",
"ora": "^3.4.0",
"p-limit": "^4.0.0"
"@babel/core": "7.12.10",
"@babel/helpers": "7.12.5",
"@babel/plugin-proposal-class-properties": "7.12.1",
"@babel/plugin-transform-regenerator": "7.12.1",
"@babel/preset-env": "7.12.11",
"@babel/preset-react": "7.12.10",
"@napi-rs/keyring": "1.3.0",
"chalk": "5.6.2",
"commander": "13.1.0",
"express": "5.2.1",
"folder-hash": "4.1.2",
"form-data": "4.0.6",
"fzstd": "0.1.1",
"glob": "8.1.0",
"glob-promise": "6.0.7",
"handlebars": "4.7.9",
"http-proxy-middleware": "3.0.5",
"inquirer": "12.11.1",
"jwt-decode": "4.0.0",
"lodash": "4.18.1",
"node-fetch": "3.3.2",
"node-watch": "0.7.4",
"open": "10.2.0",
"ora": "8.2.0",
"p-limit": "6.2.0"
},
"devDependencies": {
"prettier": "^2.6.2"
"prettier": "3.8.3",
"vitest": "2.1.9"
},
"repository": {
"type": "git",
Expand Down
Loading