15 Commits

Author SHA1 Message Date
5e7a84c6c3 v1.4.0 2026-03-02 11:47:12 +00:00
fd26379324 feat(gitea): add Actions API support: list filters, run/job endpoints, dispatch, and richer types 2026-03-02 11:47:12 +00:00
7caa033115 v1.3.0 2026-03-02 09:31:09 +00:00
2517ab863b feat(gitea): add repository branches and tags support: new IGiteaBranch/IGiteaTag interfaces and getRepoBranches/getRepoTags client methods 2026-03-02 09:31:09 +00:00
1f41870be1 v1.2.0 2026-02-28 11:13:36 +00:00
5423039f89 feat(giteaclient): add deleteRepo method to delete a repository via Gitea API 2026-02-28 11:13:36 +00:00
a34b05c55c v1.1.0 2026-02-28 10:23:51 +00:00
ffc644cab4 feat(orgs): add organization and organization-repository APIs: getOrg, getOrgRepos, createOrg, createOrgRepo 2026-02-28 10:23:51 +00:00
4c48a07ef7 v1.0.3 2026-02-24 13:00:16 +00:00
e3b013b906 fix(gitea): no changes detected in the diff; no code or doc updates 2026-02-24 13:00:16 +00:00
89bd770f32 fix(test): await async getEnvVarOnDemand calls in test setup 2026-02-24 12:59:10 +00:00
a703bc4e10 docs(core): add comprehensive readme with full API documentation 2026-02-24 12:57:27 +00:00
23e531a042 v1.0.2 2026-02-24 12:55:36 +00:00
8fc2beef78 fix(): no code changes to commit — repository unchanged 2026-02-24 12:55:36 +00:00
891e46af9b fix(core): add verdaccio as primary release registry 2026-02-24 12:55:18 +00:00
9 changed files with 559 additions and 19 deletions

View File

@@ -1,5 +1,48 @@
# Changelog
## 2026-03-02 - 1.4.0 - feat(gitea)
add Actions API support: list filters, run/job endpoints, dispatch, and richer types
- Introduce IActionRunListOptions to allow filtering action runs by status, branch, event, and actor
- Update getActionRuns to accept filtering options and build URL query parameters
- Add new client methods: getActionRun, getJobLog, rerunAction, cancelAction, and dispatchWorkflow
- Expand IGiteaActionRun and IGiteaActionRunJob shapes and add IGiteaActionRunJobStep for more detailed run/job metadata
- Export new types (IActionRunListOptions, IGiteaActionRunJobStep) from the package index
## 2026-03-02 - 1.3.0 - feat(gitea)
add repository branches and tags support: new IGiteaBranch/IGiteaTag interfaces and getRepoBranches/getRepoTags client methods
- Added IGiteaBranch and IGiteaTag interfaces (ts/gitea.interfaces.ts).
- Added getRepoBranches and getRepoTags methods with pagination support to Gitea client (ts/gitea.classes.giteaclient.ts).
- Exported the new interfaces from the package index (ts/index.ts).
## 2026-02-28 - 1.2.0 - feat(giteaclient)
add deleteRepo method to delete a repository via Gitea API
- Implements deleteRepo(owner: string, repo: string): Promise<void>
- Uses DELETE /api/v1/repos/{owner}/{repo} and encodes owner and repo with encodeURIComponent
## 2026-02-28 - 1.1.0 - feat(orgs)
add organization and organization-repository APIs: getOrg, getOrgRepos, createOrg, createOrgRepo
- Added getOrg(orgName): fetch a single organization by name
- Added getOrgRepos(orgName, opts?): list repositories in an organization with pagination, sorting by updated, and optional search query
- Added createOrg(name, opts?): create a new organization with fullName, description, and visibility (defaults to public)
- Added createOrgRepo(orgName, name, opts?): create a repository within an organization; description optional and private defaults to true
- Endpoints use encodeURIComponent for orgName in URLs to ensure safe requests
## 2026-02-24 - 1.0.3 - fix(gitea)
no changes detected in the diff; no code or doc updates
- No files changed in this commit (empty diff).
- Current package version in package.json is 1.0.2; no version bump required.
## 2026-02-24 - 1.0.2 - fix()
no code changes to commit — repository unchanged
- No files were modified according to the provided diff.
- No version bump is required; keep current version.
## 2026-02-24 - 1.0.1 - fix(repo)
no changes

View File

@@ -26,6 +26,7 @@
"@git.zone/cli": {
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"

View File

@@ -1,6 +1,6 @@
{
"name": "@apiclient.xyz/gitea",
"version": "1.0.1",
"version": "1.4.0",
"private": false,
"description": "A TypeScript client for the Gitea API, providing easy access to repositories, organizations, secrets, and action runs.",
"main": "dist_ts/index.js",

271
readme.md
View File

@@ -1,30 +1,289 @@
# @apiclient.xyz/gitea
A TypeScript client for the Gitea API, providing easy access to repositories, organizations, secrets, and action runs.
A TypeScript client for the Gitea API, providing typed access to repositories, organizations, secrets management, and Gitea Actions workflow runs.
## Install
```sh
npm install @apiclient.xyz/gitea
# or
pnpm install @apiclient.xyz/gitea
```
## Usage
All examples below use ESM imports and top-level `await`. The package ships as a pure ESM module with full TypeScript type declarations.
### Creating a Client
```typescript
import { GiteaClient } from '@apiclient.xyz/gitea';
const client = new GiteaClient('https://gitea.example.com', 'your-api-token');
```
// Test connection
The `baseUrl` should point to your Gitea instance root (trailing slashes are stripped automatically). The `token` is an API token generated in Gitea under **Settings > Applications**.
### Testing the Connection
```typescript
const result = await client.testConnection();
// List repositories
if (result.ok) {
console.log('Connected successfully.');
} else {
console.error('Connection failed:', result.error);
}
```
### Listing Repositories
```typescript
// Fetch the first page of repositories (default: 50 per page, sorted by updated)
const repos = await client.getRepos();
// Manage secrets
await client.setRepoSecret('owner/repo', 'SECRET_KEY', 'secret-value');
// Search with pagination
const results = await client.getRepos({
search: 'my-project',
page: 1,
perPage: 20,
});
for (const repo of results) {
console.log(`${repo.full_name} - ${repo.description}`);
}
```
### Listing Organizations
```typescript
const orgs = await client.getOrgs();
for (const org of orgs) {
console.log(`${org.name} (${org.repo_count} repos)`);
}
// With pagination
const page2 = await client.getOrgs({ page: 2, perPage: 10 });
```
### Managing Repository Secrets
```typescript
const ownerRepo = 'my-org/my-repo';
// List all secrets for a repository
const secrets = await client.getRepoSecrets(ownerRepo);
for (const secret of secrets) {
console.log(`${secret.name} (created ${secret.created_at})`);
}
// Create or update a secret
await client.setRepoSecret(ownerRepo, 'DEPLOY_TOKEN', 's3cret-value');
// Delete a secret
await client.deleteRepoSecret(ownerRepo, 'DEPLOY_TOKEN');
```
### Managing Organization Secrets
```typescript
const orgName = 'my-org';
// List all secrets for an organization
const orgSecrets = await client.getOrgSecrets(orgName);
// Create or update an organization-level secret
await client.setOrgSecret(orgName, 'NPM_TOKEN', 'npm_abc123');
// Delete an organization-level secret
await client.deleteOrgSecret(orgName, 'NPM_TOKEN');
```
### Working with Action Runs
```typescript
const ownerRepo = 'my-org/my-repo';
// List recent action runs
const runs = await client.getActionRuns(ownerRepo);
for (const run of runs) {
console.log(`#${run.id} ${run.name} [${run.status}/${run.conclusion}] on ${run.head_branch}`);
}
// Paginate runs
const olderRuns = await client.getActionRuns(ownerRepo, { page: 2, perPage: 10 });
// Get jobs for a specific run
const jobs = await client.getActionRunJobs(ownerRepo, runs[0].id);
for (const job of jobs) {
console.log(` Job "${job.name}" - ${job.status} (${job.run_duration}s)`);
}
// Fetch raw log output for a job
const log = await client.getJobLog(ownerRepo, jobs[0].id);
console.log(log);
// Re-run a workflow
await client.rerunAction(ownerRepo, runs[0].id);
// Cancel a running workflow
await client.cancelAction(ownerRepo, runs[0].id);
```
## API Reference
### `GiteaClient`
| Method | Signature | Returns | Description |
|--------|-----------|---------|-------------|
| `constructor` | `(baseUrl: string, token: string)` | `GiteaClient` | Create a new client instance. |
| `testConnection` | `()` | `Promise<ITestConnectionResult>` | Verify credentials and connectivity. |
| `getRepos` | `(opts?: IListOptions)` | `Promise<IGiteaRepository[]>` | Search/list repositories with pagination. |
| `getOrgs` | `(opts?: IListOptions)` | `Promise<IGiteaOrganization[]>` | List organizations with pagination. |
| `getRepoSecrets` | `(ownerRepo: string)` | `Promise<IGiteaSecret[]>` | List all action secrets for a repository. |
| `setRepoSecret` | `(ownerRepo: string, key: string, value: string)` | `Promise<void>` | Create or update a repository secret. |
| `deleteRepoSecret` | `(ownerRepo: string, key: string)` | `Promise<void>` | Delete a repository secret. |
| `getOrgSecrets` | `(orgName: string)` | `Promise<IGiteaSecret[]>` | List all action secrets for an organization. |
| `setOrgSecret` | `(orgName: string, key: string, value: string)` | `Promise<void>` | Create or update an organization secret. |
| `deleteOrgSecret` | `(orgName: string, key: string)` | `Promise<void>` | Delete an organization secret. |
| `getActionRuns` | `(ownerRepo: string, opts?: IListOptions)` | `Promise<IGiteaActionRun[]>` | List action workflow runs for a repository. |
| `getActionRunJobs` | `(ownerRepo: string, runId: number)` | `Promise<IGiteaActionRunJob[]>` | List jobs within a specific action run. |
| `getJobLog` | `(ownerRepo: string, jobId: number)` | `Promise<string>` | Fetch the raw log output for a job. |
| `rerunAction` | `(ownerRepo: string, runId: number)` | `Promise<void>` | Re-run a completed action run. |
| `cancelAction` | `(ownerRepo: string, runId: number)` | `Promise<void>` | Cancel an in-progress action run. |
### Parameter Conventions
- **`ownerRepo`** -- Repository identifier in `"owner/repo"` format (e.g., `"my-org/my-repo"`).
- **`orgName`** -- Organization login name (e.g., `"my-org"`).
- **`key`** -- Secret name, typically uppercase with underscores (e.g., `"DEPLOY_TOKEN"`).
- **`runId` / `jobId`** -- Numeric identifiers returned by the list endpoints.
## Interfaces
All interfaces are exported from the package entry point and can be imported for type annotations.
```typescript
import type {
IGiteaUser,
IGiteaRepository,
IGiteaOrganization,
IGiteaSecret,
IGiteaActionRun,
IGiteaActionRunJob,
ITestConnectionResult,
IListOptions,
} from '@apiclient.xyz/gitea';
```
### `IListOptions`
Pagination and search options accepted by list methods.
```typescript
interface IListOptions {
search?: string; // Free-text search query (repos only)
page?: number; // Page number (default: 1)
perPage?: number; // Results per page (default: 50 for repos/orgs, 30 for runs)
}
```
### `ITestConnectionResult`
```typescript
interface ITestConnectionResult {
ok: boolean; // true if the connection succeeded
error?: string; // Error message when ok is false
}
```
### `IGiteaUser`
```typescript
interface IGiteaUser {
id: number;
login: string;
full_name: string;
email: string;
avatar_url: string;
}
```
### `IGiteaRepository`
```typescript
interface IGiteaRepository {
id: number;
name: string;
full_name: string; // "owner/repo"
description: string;
default_branch: string;
html_url: string;
private: boolean;
topics: string[];
updated_at: string; // ISO 8601 timestamp
owner: {
id: number;
login: string;
avatar_url: string;
};
}
```
### `IGiteaOrganization`
```typescript
interface IGiteaOrganization {
id: number;
name: string;
full_name: string;
description: string;
visibility: string; // "public", "limited", or "private"
repo_count: number;
}
```
### `IGiteaSecret`
```typescript
interface IGiteaSecret {
name: string; // Secret key name
created_at: string; // ISO 8601 timestamp
}
```
### `IGiteaActionRun`
```typescript
interface IGiteaActionRun {
id: number;
name: string; // Workflow name
status: string; // "running", "waiting", "success", "failure", etc.
conclusion: string; // Final result: "success", "failure", "cancelled", etc.
head_branch: string; // Branch that triggered the run
head_sha: string; // Commit SHA
html_url: string; // Web URL for the run
event: string; // Trigger event: "push", "pull_request", etc.
run_duration: number; // Duration in seconds
created_at: string; // ISO 8601 timestamp
}
```
### `IGiteaActionRunJob`
```typescript
interface IGiteaActionRunJob {
id: number;
name: string; // Job name from the workflow
status: string;
conclusion: string;
run_duration: number; // Duration in seconds
}
```
## License
MIT
MIT -- see [LICENSE](./license.md) for details.
Published under the [Lossless GmbH](https://lossless.com) umbrella.

View File

@@ -7,8 +7,8 @@ const testQenv = new qenv.Qenv('./', '.nogit/');
let giteaClient: GiteaClient;
tap.test('should create a GiteaClient instance', async () => {
const baseUrl = testQenv.getEnvVarOnDemand('GITEA_BASE_URL') || 'https://gitea.lossless.digital';
const token = testQenv.getEnvVarOnDemand('GITEA_TOKEN') || '';
const baseUrl = (await testQenv.getEnvVarOnDemand('GITEA_BASE_URL')) || 'https://gitea.lossless.digital';
const token = (await testQenv.getEnvVarOnDemand('GITEA_TOKEN')) || '';
giteaClient = new GiteaClient(baseUrl, token);
expect(giteaClient).toBeInstanceOf(GiteaClient);
});

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@apiclient.xyz/gitea',
version: '1.0.1',
version: '1.4.0',
description: 'A TypeScript client for the Gitea API, providing easy access to repositories, organizations, secrets, and action runs.'
}

View File

@@ -5,10 +5,13 @@ import type {
IGiteaRepository,
IGiteaOrganization,
IGiteaSecret,
IGiteaBranch,
IGiteaTag,
IGiteaActionRun,
IGiteaActionRunJob,
ITestConnectionResult,
IListOptions,
IActionRunListOptions,
} from './gitea.interfaces.js';
export class GiteaClient {
@@ -149,6 +152,78 @@ export class GiteaClient {
return this.request<IGiteaOrganization[]>('GET', `/api/v1/orgs?page=${page}&limit=${limit}`);
}
/**
* Get a single organization by name
*/
public async getOrg(orgName: string): Promise<IGiteaOrganization> {
return this.request<IGiteaOrganization>('GET', `/api/v1/orgs/${encodeURIComponent(orgName)}`);
}
/**
* List repositories within an organization
*/
public async getOrgRepos(orgName: string, opts?: IListOptions): Promise<IGiteaRepository[]> {
const page = opts?.page || 1;
const limit = opts?.perPage || 50;
let url = `/api/v1/orgs/${encodeURIComponent(orgName)}/repos?page=${page}&limit=${limit}&sort=updated`;
if (opts?.search) {
url += `&q=${encodeURIComponent(opts.search)}`;
}
return this.request<IGiteaRepository[]>('GET', url);
}
/**
* Create a new organization
*/
public async createOrg(name: string, opts?: {
fullName?: string;
description?: string;
visibility?: string;
}): Promise<IGiteaOrganization> {
return this.request<IGiteaOrganization>('POST', '/api/v1/orgs', {
username: name,
full_name: opts?.fullName || name,
description: opts?.description || '',
visibility: opts?.visibility || 'public',
});
}
/**
* Create a repository within an organization
*/
public async createOrgRepo(orgName: string, name: string, opts?: {
description?: string;
private?: boolean;
}): Promise<IGiteaRepository> {
return this.request<IGiteaRepository>('POST', `/api/v1/orgs/${encodeURIComponent(orgName)}/repos`, {
name,
description: opts?.description || '',
private: opts?.private ?? true,
});
}
// ---------------------------------------------------------------------------
// Repository Branches & Tags
// ---------------------------------------------------------------------------
public async getRepoBranches(ownerRepo: string, opts?: IListOptions): Promise<IGiteaBranch[]> {
const page = opts?.page || 1;
const limit = opts?.perPage || 50;
return this.request<IGiteaBranch[]>(
'GET',
`/api/v1/repos/${ownerRepo}/branches?page=${page}&limit=${limit}`,
);
}
public async getRepoTags(ownerRepo: string, opts?: IListOptions): Promise<IGiteaTag[]> {
const page = opts?.page || 1;
const limit = opts?.perPage || 50;
return this.request<IGiteaTag[]>(
'GET',
`/api/v1/repos/${ownerRepo}/tags?page=${page}&limit=${limit}`,
);
}
// ---------------------------------------------------------------------------
// Repository Secrets
// ---------------------------------------------------------------------------
@@ -185,27 +260,85 @@ export class GiteaClient {
// Action Runs
// ---------------------------------------------------------------------------
public async getActionRuns(ownerRepo: string, opts?: IListOptions): Promise<IGiteaActionRun[]> {
/**
* List action runs for a repository with optional filters.
* Supports status, branch, event, actor filtering.
*/
public async getActionRuns(ownerRepo: string, opts?: IActionRunListOptions): Promise<IGiteaActionRun[]> {
const page = opts?.page || 1;
const limit = opts?.perPage || 30;
const body = await this.request<any>('GET', `/api/v1/repos/${ownerRepo}/actions/runs?page=${page}&limit=${limit}`);
let url = `/api/v1/repos/${ownerRepo}/actions/runs?page=${page}&limit=${limit}`;
if (opts?.status) url += `&status=${encodeURIComponent(opts.status)}`;
if (opts?.branch) url += `&branch=${encodeURIComponent(opts.branch)}`;
if (opts?.event) url += `&event=${encodeURIComponent(opts.event)}`;
if (opts?.actor) url += `&actor=${encodeURIComponent(opts.actor)}`;
const body = await this.request<any>('GET', url);
return body.workflow_runs || body;
}
/**
* Get a single action run's full details.
*/
public async getActionRun(ownerRepo: string, runId: number): Promise<IGiteaActionRun> {
return this.request<IGiteaActionRun>(
'GET',
`/api/v1/repos/${ownerRepo}/actions/runs/${runId}`,
);
}
/**
* List jobs for an action run.
*/
public async getActionRunJobs(ownerRepo: string, runId: number): Promise<IGiteaActionRunJob[]> {
const body = await this.request<any>('GET', `/api/v1/repos/${ownerRepo}/actions/runs/${runId}/jobs`);
return body.jobs || body;
}
/**
* Get a job's raw log output.
*/
public async getJobLog(ownerRepo: string, jobId: number): Promise<string> {
return this.requestText('GET', `/api/v1/repos/${ownerRepo}/actions/jobs/${jobId}/logs`);
}
/**
* Re-run an action run.
*/
public async rerunAction(ownerRepo: string, runId: number): Promise<void> {
await this.request('POST', `/api/v1/repos/${ownerRepo}/actions/runs/${runId}/rerun`);
}
/**
* Cancel a running action run.
*/
public async cancelAction(ownerRepo: string, runId: number): Promise<void> {
await this.request('POST', `/api/v1/repos/${ownerRepo}/actions/runs/${runId}/cancel`);
}
/**
* Dispatch a workflow (trigger manually).
*/
public async dispatchWorkflow(
ownerRepo: string,
workflowId: string,
ref: string,
inputs?: Record<string, string>,
): Promise<void> {
await this.request(
'POST',
`/api/v1/repos/${ownerRepo}/actions/workflows/${encodeURIComponent(workflowId)}/dispatches`,
{ ref, inputs: inputs || {} },
);
}
// ---------------------------------------------------------------------------
// Repository Deletion
// ---------------------------------------------------------------------------
public async deleteRepo(owner: string, repo: string): Promise<void> {
await this.request(
'DELETE',
`/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
);
}
}

View File

@@ -1,3 +1,37 @@
// ---------------------------------------------------------------------------
// Common
// ---------------------------------------------------------------------------
export interface ITestConnectionResult {
ok: boolean;
error?: string;
}
export interface IListOptions {
search?: string;
page?: number;
perPage?: number;
}
// ---------------------------------------------------------------------------
// Action Run list options
// ---------------------------------------------------------------------------
export interface IActionRunListOptions extends IListOptions {
/** Filter by run status (waiting, running, success, failure, cancelled) */
status?: string;
/** Filter by head branch */
branch?: string;
/** Filter by trigger event (push, pull_request, schedule, workflow_dispatch, …) */
event?: string;
/** Filter by the user who triggered the run */
actor?: string;
}
// ---------------------------------------------------------------------------
// Users
// ---------------------------------------------------------------------------
export interface IGiteaUser {
id: number;
login: string;
@@ -6,6 +40,10 @@ export interface IGiteaUser {
avatar_url: string;
}
// ---------------------------------------------------------------------------
// Repositories
// ---------------------------------------------------------------------------
export interface IGiteaRepository {
id: number;
name: string;
@@ -23,6 +61,10 @@ export interface IGiteaRepository {
};
}
// ---------------------------------------------------------------------------
// Organizations
// ---------------------------------------------------------------------------
export interface IGiteaOrganization {
id: number;
name: string;
@@ -32,39 +74,97 @@ export interface IGiteaOrganization {
repo_count: number;
}
// ---------------------------------------------------------------------------
// Secrets
// ---------------------------------------------------------------------------
export interface IGiteaSecret {
name: string;
created_at: string;
}
// ---------------------------------------------------------------------------
// Action Runs
// ---------------------------------------------------------------------------
export interface IGiteaActionRun {
id: number;
name: string;
workflow_id: string;
status: string;
conclusion: string;
head_branch: string;
head_sha: string;
html_url: string;
event: string;
run_number: number;
run_attempt: number;
run_duration: number;
created_at: string;
updated_at: string;
started_at: string;
actor: IGiteaUser;
trigger_actor: IGiteaUser;
repository: {
id: number;
name: string;
full_name: string;
html_url: string;
};
head_commit: {
id: string;
message: string;
timestamp: string;
};
}
// ---------------------------------------------------------------------------
// Action Run Jobs
// ---------------------------------------------------------------------------
export interface IGiteaActionRunJob {
id: number;
run_id: number;
name: string;
workflow_name: string;
head_branch: string;
head_sha: string;
status: string;
conclusion: string;
html_url: string;
run_duration: number;
started_at: string;
completed_at: string;
steps: IGiteaActionRunJobStep[];
labels: string[];
runner_id: number;
runner_name: string;
}
export interface ITestConnectionResult {
ok: boolean;
error?: string;
export interface IGiteaActionRunJobStep {
name: string;
number: number;
status: string;
conclusion: string;
started_at: string;
completed_at: string;
}
export interface IListOptions {
search?: string;
page?: number;
perPage?: number;
// ---------------------------------------------------------------------------
// Branches & Tags
// ---------------------------------------------------------------------------
export interface IGiteaBranch {
name: string;
commit: {
id: string;
};
}
export interface IGiteaTag {
name: string;
id: string;
commit: {
sha: string;
};
}

View File

@@ -4,9 +4,13 @@ export type {
IGiteaRepository,
IGiteaOrganization,
IGiteaSecret,
IGiteaBranch,
IGiteaTag,
IGiteaActionRun,
IGiteaActionRunJob,
IGiteaActionRunJobStep,
ITestConnectionResult,
IListOptions,
IActionRunListOptions,
} from './gitea.interfaces.js';
export { commitinfo } from './00_commitinfo_data.js';