Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
65 changes: 58 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name: CI & Quality Gate

on:
workflow_dispatch:
push:
branches:
- main
- develop
paths:
- 'packages/**'
- 'bin/**'
Expand All @@ -14,7 +13,10 @@ on:
- 'turbo.json'
- '.github/workflows/ci.yml'
pull_request:
types: [opened, synchronize, reopened]
types: [opened, synchronize, reopened, labeled]
branches:
- main
- develop
paths:
- 'packages/**'
- 'bin/**'
Expand Down Expand Up @@ -112,7 +114,7 @@ jobs:
name: Publish Independent Packages
needs: sonarcloud
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
permissions:
contents: write
packages: write
Expand Down Expand Up @@ -170,19 +172,68 @@ jobs:
fi

- name: Publish all changed packages
run: yarn publish:all
run: |
if [ "${{ github.ref }}" = "refs/heads/develop" ]; then
yarn publish:all --tag latest-dev
else
yarn publish:all
fi

- name: Sync with remote to prevent push rejection
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git fetch origin main
git pull --rebase origin main --autostash
BRANCH="${GITHUB_REF_NAME:-main}"
git fetch origin "$BRANCH"
git pull --rebase origin "$BRANCH" --autostash

- name: Commit version bumps & hashes
uses: stefanzweifel/git-auto-commit-action@v5 # NOSONAR
with:
commit_message: "chore: release versions & update hashes [skip ci]"
commit_options: '--no-verify'

publish-preview:
name: Publish On-Demand QA Preview Packages
needs: sonarcloud
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && (contains(github.event.pull_request.labels.*.name, 'qa:preview') || contains(github.event.pull_request.labels.*.name, 'preview:publish'))
permissions:
contents: read
packages: write
pull-requests: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Enable Corepack
run: corepack enable

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22

- name: Cache Yarn dependencies
uses: actions/cache@v4
with:
path: .yarn/cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Install dependencies
run: YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install

- name: Publish Preview Packages via Shared Action
uses: Quatrain/actions/publish-package-preview@main
with:
pr_number: ${{ github.event.pull_request.number }}
npm_token: ${{ secrets.NPM_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
script_path: 'bin/publish_all.js'


48 changes: 31 additions & 17 deletions bin/publish_all.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ async function publishAll() {
}

const forceBuild = process.argv.includes('--force');
const prArgIndex = process.argv.indexOf('--pr');
const prNum = prArgIndex !== -1 ? process.argv[prArgIndex + 1] : null;
const tagArgIndex = process.argv.indexOf('--tag');
const npmTag = tagArgIndex !== -1 ? process.argv[tagArgIndex + 1] : 'latest';
const npmTag = tagArgIndex !== -1 ? process.argv[tagArgIndex + 1] : (prNum ? `pr${prNum}` : 'latest');
const tagString = npmTag ? `--tag ${npmTag}` : '';

if (!anyPackageChanged && !forceBuild) {
Expand Down Expand Up @@ -132,17 +134,26 @@ async function publishAll() {
const hash = computedHashes[pkgName];
const previousData = previousDataMap[pkgName];

if (previousData.hash !== hash) {
if (previousData.hash !== hash || prNum) {
console.log(`[PUBLISH] Changes detected in ${pkgName}. Releasing...`);

try {
// Execute standard release pipeline
runSync('yarn', ['version', 'patch'], { cwd: pkgDir, stdio: 'inherit' });

// Read new version and keep original content
let newVersion;
let updatedPkgJson;
const originalPkgContent = fs.readFileSync(pkgJsonPath, 'utf8');
const updatedPkgJson = JSON.parse(originalPkgContent);
const newVersion = updatedPkgJson.version;

if (prNum) {
const baseVersion = pkgJson.version.split('-')[0];
newVersion = `${baseVersion}-pr${prNum}.${Date.now().toString().slice(-4)}`;
updatedPkgJson = JSON.parse(originalPkgContent);
updatedPkgJson.version = newVersion;
} else {
// Execute standard release pipeline
runSync('yarn', ['version', 'patch'], { cwd: pkgDir, stdio: 'inherit' });
const bumpedContent = fs.readFileSync(pkgJsonPath, 'utf8');
updatedPkgJson = JSON.parse(bumpedContent);
newVersion = updatedPkgJson.version;
}

// Strip workspace: protocol before packing
['dependencies', 'devDependencies', 'peerDependencies'].forEach(deptype => {
Expand Down Expand Up @@ -215,18 +226,21 @@ async function publishAll() {
if (fs.existsSync(path.join(pkgDir, '.npmignore'))) fs.unlinkSync(path.join(pkgDir, '.npmignore'));
}

// Keep registry updated with the stable hash
registry[pkgName] = {
version: newVersion,
hash: hash,
last_published: new Date().toISOString()
};
// Keep registry updated with the stable hash (only for official releases)
if (!prNum) {
registry[pkgName] = {
version: newVersion,
hash: hash,
last_published: new Date().toISOString()
};
changed = true;
}

changed = true;
console.log(`[PUBLISH] Success for ${pkgName} v${newVersion}`);
console.log(`[PUBLISH] Success for ${pkgName} v${newVersion} (tag: ${npmTag})`);
publishedPackages.push({
Package: pkgName,
Version: newVersion
Version: newVersion,
Tag: npmTag
});

} catch (error) {
Expand Down
35 changes: 35 additions & 0 deletions docs/pages/packages/api-server-astro/howto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# HOWTO: Using @quatrain/api-server-astro

This document guides you on routing API endpoints through Astro.

---

## 1. Catch-all Routing in Astro

Create a catch-all server endpoint in Astro (e.g. `src/pages/api/[...path].ts`) and bind the AstroAdapter:

```typescript
import { AstroAdapter } from '@quatrain/api-server-astro';
import { setupApiServer } from '../your-api-setup'; // Your API router configuration

const adapter = new AstroAdapter('/api');
setupApiServer(adapter);

// Export Astro APIRoute handlers
export const ALL = adapter.handle();
```

## 2. Wrapping a single handler

If you only want to wrap a single Quatrain API handler as an Astro APIRoute:

```typescript
import { AstroAdapter } from '@quatrain/api-server-astro';
import { ApiRequest, ApiResponse } from '@quatrain/api';

const myHandler = async (req: ApiRequest, res: ApiResponse) => {
res.json({ message: 'Hello from Astro!' });
};

export const GET = AstroAdapter.wrap(myHandler);
```
19 changes: 19 additions & 0 deletions docs/pages/packages/api-server-astro/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @quatrain/api-server-astro

Astro Adapter for the Quatrain API Server. It bridges the Quatrain API server interface with the web standard Request/Response API used natively by Astro endpoints.

## Features

- **Standard Astro APIRoute compatibility**: Easily host Quatrain API handlers inside Astro server routes.
- **Express-like Route Parsing**: Supports catch-all routes and extracts route parameters dynamically.
- **Response Recording**: Records Quatrain API responses and translates them to native Astro standard Responses.

---

## Getting Started

Refer to `HOWTO.md` for integration details.

## License

AGPL-3.0-only
33 changes: 33 additions & 0 deletions docs/pages/packages/api-xmlrpc/howto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# HOWTO: Using @quatrain/api-xmlrpc

This document shows how to initialize and use the XML-RPC client wrapper.

---

## 1. Initializing the Client

Provide target connection options to instantiate `XmlRpcClient`:

```typescript
import { XmlRpcClient } from '@quatrain/api-xmlrpc';

const client = new XmlRpcClient({
host: 'odoo.example.com',
port: 443,
path: '/xmlrpc/2/common',
secure: true
});
```

## 2. Invoking Remote Methods

Use the `methodCall` method to execute calls asynchronously. It returns a Promise:

```typescript
try {
const version = await client.methodCall('version', []);
console.log('Odoo Version Details:', version);
} catch (err) {
console.error('Connection failed:', err);
}
```
19 changes: 19 additions & 0 deletions docs/pages/packages/api-xmlrpc/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @quatrain/api-xmlrpc

An XML-RPC client package designed for the Quatrain Core framework. It provides a simple, Promise-based wrapper around the XML-RPC protocol.

## Features

- **Promise-based API**: Replaces node-style callback interfaces with modern async/await patterns.
- **Support for secure connections**: Easily toggle secure HTTPS execution.
- **Seamless integration**: Built specifically to connect with external systems utilizing the XML-RPC protocol (e.g. Odoo).

---

## Getting Started

Refer to the `HOWTO.md` file for code examples and configuration details.

## License

AGPL-3.0-only
Loading
Loading