Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ The upload flow has two stages handled by two classes, with a shared `MarkerPars

Composable fetch wrappers using higher-order functions:

- `utils.ts` — `withBaseUrl`, `withApiKey`, `withJson` decorators that wrap `fetch`
- `index.ts` — `createApi(baseUrl, apiKey)` assembles the API client from sub-modules
- `utils.ts` — `withBaseUrl`, `withAuth`, `withJson` decorators that wrap `fetch`
- `index.ts` — `createApi(baseUrl, token, authType)` assembles the API client from sub-modules
- Sub-modules: `projects.ts`, `run.ts`, `tcases.ts`, `file.ts`

### Configuration (src/utils/)
Expand Down
43 changes: 30 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,46 @@ Verify installation: `qasphere --version`

**Update:** Run `npm update -g qas-cli` to get the latest version.

## Environment
## Authentication

The CLI requires the following variables to be defined:
The recommended way to authenticate is using the interactive login command:

- `QAS_TOKEN` - QA Sphere API token (see [docs](https://docs.qasphere.com/api/authentication) if you need help generating one)
- `QAS_URL` - Base URL of your QA Sphere instance (e.g., `https://qas.eu2.qasphere.com`)
```bash
qasphere auth login
```

This opens your browser to complete authentication and securely stores your credentials in the system keyring. If a keyring is not available, credentials are stored in `~/.config/qasphere/credentials.json` with restricted file permissions.

### Other auth commands

```bash
qasphere auth status # Show current authentication status
qasphere auth logout # Clear stored credentials
```

### Credential resolution order

The CLI resolves credentials in the following order (first match wins):

1. `QAS_TOKEN` and `QAS_URL` environment variables
2. `.env` file in the current working directory
3. System keyring (set by `qasphere auth login`)
4. `~/.config/qasphere/credentials.json` (fallback when keyring is unavailable)
5. `.qaspherecli` file in the current directory or any parent directory

These variables could be defined:
### Manual configuration

- as environment variables
- in .env of a current working directory
- in a special `.qaspherecli` configuration file in your project directory (or any parent directory)
Instead of using `auth login`, you can manually set the required variables:

Example: .qaspherecli
- `QAS_TOKEN` - QA Sphere API token (see [docs](https://docs.qasphere.com/api/authentication) if you need help generating one)
- `QAS_URL` - Base URL of your QA Sphere instance (e.g., `https://qas.eu2.qasphere.com`)

These variables can be defined as environment variables, in a `.env` file, or in a `.qaspherecli` configuration file:

```sh
# .qaspherecli
QAS_TOKEN=your_token
QAS_URL=https://qas.eu1.qasphere.com

# Example with real values:
# QAS_TOKEN=qas.1CKCEtest_JYyckc3zYtest.dhhjYY3BYEoQH41e62itest
# QAS_URL=https://qas.eu1.qasphere.com
```

## Commands: `junit-upload`, `playwright-json-upload`, `allure-upload`
Expand Down
239 changes: 237 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"vitest": "^3.1.2"
},
"dependencies": {
"@napi-rs/keyring": "^1.2.0",
"chalk": "^5.4.1",
"dotenv": "^16.5.0",
"escape-html": "^1.0.3",
Expand Down
16 changes: 9 additions & 7 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { createFolderApi } from './folders'
import { createProjectApi } from './projects'
import { createRunApi } from './run'
import { createTCaseApi } from './tcases'
import { withBaseUrl, withHeaders, withHttpRetry } from './utils'
import { withFetchMiddlewares, withBaseUrl, withAuth, withUserAgent, withHttpRetry } from './utils'
import type { AuthType } from './utils'
import { CLI_VERSION } from '../utils/version'

const getApi = (fetcher: typeof fetch) => {
Expand All @@ -18,12 +19,13 @@ const getApi = (fetcher: typeof fetch) => {

export type Api = ReturnType<typeof getApi>

export const createApi = (baseUrl: string, apiKey: string) =>
export const createApi = (baseUrl: string, token: string, authType: AuthType = 'apikey') =>
getApi(
withHttpRetry(
withHeaders(withBaseUrl(fetch, baseUrl), {
Authorization: `ApiKey ${apiKey}`,
'User-Agent': `qas-cli/${CLI_VERSION}`,
})
withFetchMiddlewares(
fetch,
withBaseUrl(baseUrl),
withUserAgent(CLI_VERSION),
withAuth(token, authType),
withHttpRetry
)
)
Loading
Loading