chore(ci): harden and split workflows (#539)
Some checks are pending
CI / DSH plugin / Validate DSH plugin (push) Waiting to run
CI / OpenClaw plugin / Validate OpenClaw plugin (push) Waiting to run
CI / Python packages / Build and verify distributions (push) Waiting to run
CI / Python quality / GitHub Actions (push) Waiting to run
CI / Python quality / Pre-commit (push) Waiting to run
CI / Python tests / Unit Tests - py3.13 (push) Waiting to run
CI / Python tests / Unit Tests - py3.14 (push) Waiting to run
CI / Python tests / Unit Tests - py3.11 (push) Waiting to run
CI / Python tests / Unit Tests - py3.12 (push) Waiting to run
CI / ReMe Studio / Studio checks (push) Waiting to run
CI / Windows / CLI smoke - py3.11 (push) Waiting to run
Deploy / Documentation / Build documentation (push) Waiting to run
Deploy / Documentation / deploy (push) Blocked by required conditions
Security / CodeQL / Analyze javascript-typescript (push) Waiting to run
Security / CodeQL / Analyze python (push) Waiting to run

* chore(ci): harden and split workflows

* fix(ci): support token-based npm publishing

* test: make disappearing resource check portable

* fix(ci): make Studio releases recoverable

* fix(ci): stop Studio publishing on cancellation
This commit is contained in:
jinliyl 2026-09-11 18:21:49 +08:00 committed by GitHub
parent 05958d4d8b
commit 9ad3dafce5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 587 additions and 315 deletions

View file

@ -1,5 +1,18 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "main"
schedule:
interval: "weekly"
day: "monday"
time: "09:30"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 5
commit-message:
prefix: "chore"
include: "scope"
- package-ecosystem: "pip"
directory: "/"
target-branch: "main"

View file

@ -21,18 +21,19 @@ jobs:
build:
name: Build documentation
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: github-pages
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.22.3'
cache: npm
@ -54,6 +55,6 @@ jobs:
- name: Upload Pages artifact
if: inputs.upload_pages_artifact
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: github-pages/dist

View file

@ -21,14 +21,15 @@ jobs:
distributions:
name: Build Python distributions
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.11'
@ -81,7 +82,7 @@ jobs:
- name: Upload ReMe distributions
if: inputs.upload_artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: reme-distributions
path: dist/reme/

View file

@ -0,0 +1,155 @@
name: _Release npm plugin
on:
workflow_call:
inputs:
directory:
description: Repository-relative package directory
required: true
type: string
package_name:
description: Exact public npm package name
required: true
type: string
artifact_name:
description: Prefix for the packed package artifact
required: true
type: string
version:
description: Exact package.json version; an optional v prefix is accepted
required: true
type: string
npm_tag:
description: npm distribution tag
required: true
type: string
use_npm_token:
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
required: false
default: false
type: boolean
validate_clawhub:
description: Validate the package against the ClawHub contract
required: false
default: false
type: boolean
outputs:
version:
description: Normalized package version
value: ${{ jobs.build.outputs.version }}
secrets:
NPM_TOKEN:
description: Optional bootstrap or recovery token for npm publishing
required: false
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
version: ${{ steps.validate.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.16.0"
cache: npm
cache-dependency-path: ${{ inputs.directory }}/package-lock.json
- name: Validate package identity and version
id: validate
working-directory: ${{ inputs.directory }}
env:
EXPECTED_NAME: ${{ inputs.package_name }}
RELEASE_VERSION: ${{ inputs.version }}
NPM_TAG: ${{ inputs.npm_tag }}
run: |
node --input-type=module <<'JS'
import { appendFileSync, readFileSync } from 'node:fs';
const manifest = JSON.parse(readFileSync('package.json', 'utf8'));
const expected = process.env.RELEASE_VERSION.replace(/^v/, '');
if (manifest.name !== process.env.EXPECTED_NAME) {
throw new Error(`Expected ${process.env.EXPECTED_NAME}, found ${manifest.name}`);
}
if (manifest.version !== expected) throw new Error(`package.json is ${manifest.version}, workflow input is ${expected}`);
if (manifest.version.includes('-') !== (process.env.NPM_TAG === 'next')) {
throw new Error('Prereleases must use next; stable releases must use latest');
}
appendFileSync(process.env.GITHUB_OUTPUT, `version=${manifest.version}\n`);
JS
- run: npm ci
working-directory: ${{ inputs.directory }}
- name: Validate package
working-directory: ${{ inputs.directory }}
run: |
npm run format:check
npm run lint
npm run typecheck
npm test
npm run test:package
- name: Validate ClawHub contract
if: inputs.validate_clawhub
working-directory: ${{ inputs.directory }}
run: npx --yes clawhub@0.23.3 package validate . --json
- name: Pack
working-directory: ${{ inputs.directory }}
run: |
mkdir -p "$RUNNER_TEMP/plugin-package"
npm pack --pack-destination "$RUNNER_TEMP/plugin-package"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ inputs.artifact_name }}-${{ steps.validate.outputs.version }}
path: ${{ runner.temp }}/plugin-package/*.tgz
if-no-files-found: error
publish:
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
registry-url: https://registry.npmjs.org
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ inputs.artifact_name }}-${{ needs.build.outputs.version }}
path: dist/plugin
- name: Reject an existing package version
env:
PACKAGE_NAME: ${{ inputs.package_name }}
PACKAGE_VERSION: ${{ needs.build.outputs.version }}
run: |
if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${PACKAGE_VERSION} already exists" >&2
exit 1
fi
- name: Publish to npm with Trusted Publishing
if: ${{ !inputs.use_npm_token }}
env:
NPM_TAG: ${{ inputs.npm_tag }}
run: npm publish dist/plugin/*.tgz --access public --tag "$NPM_TAG" --provenance
- name: Publish to npm with NPM_TOKEN
if: inputs.use_npm_token
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TAG: ${{ inputs.npm_tag }}
run: |
if [[ -z "${NODE_AUTH_TOKEN}" ]]; then
echo "NPM_TOKEN is required when use_npm_token is enabled" >&2
exit 1
fi
npm publish dist/plugin/*.tgz --access public --tag "$NPM_TAG" --provenance

View file

@ -1,29 +1,8 @@
name: CI / Documentation
on:
push:
branches: [main, master, dev, develop]
paths:
- '.github/workflows/ci-docs.yml'
- '.github/workflows/_build-docs.yml'
- 'AGENTS.md'
- 'README.md'
- 'README_ZH.md'
- 'docs/**'
- 'github-pages/**'
- 'reme/config/default.yaml'
- 'integrations/claude_code/README.md'
- 'integrations/hermes_agent/README.md'
- 'reme_studio/README*.md'
- 'reme_studio/public/og.jpg'
- 'integrations/dsh/README*.md'
- 'integrations/dsh/figures/**'
- 'integrations/openclaw/README*.md'
- 'plugins/*/README*.md'
- 'benchmark/*/README*.md'
- 'benchmark/toolmemory/gitcha.png'
pull_request:
branches: [main, master, dev, develop]
branches: [main]
paths:
- '.github/workflows/ci-docs.yml'
- '.github/workflows/_build-docs.yml'

52
.github/workflows/ci-dsh-plugin.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: CI / DSH plugin
on:
push:
branches: [main]
paths:
- ".github/workflows/ci-dsh-plugin.yml"
- ".github/workflows/_release-npm-plugin.yml"
- ".github/workflows/release-dsh-plugin.yml"
- "integrations/dsh/**"
pull_request:
branches: [main]
paths:
- ".github/workflows/ci-dsh-plugin.yml"
- ".github/workflows/_release-npm-plugin.yml"
- ".github/workflows/release-dsh-plugin.yml"
- "integrations/dsh/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
package:
name: Validate DSH plugin
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: integrations/dsh
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.16.0"
cache: npm
cache-dependency-path: integrations/dsh/package-lock.json
- run: npm ci
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run test:package

View file

@ -0,0 +1,54 @@
name: CI / OpenClaw plugin
on:
push:
branches: [main]
paths:
- ".github/workflows/ci-openclaw-plugin.yml"
- ".github/workflows/_release-npm-plugin.yml"
- ".github/workflows/release-openclaw-plugin.yml"
- "integrations/openclaw/**"
pull_request:
branches: [main]
paths:
- ".github/workflows/ci-openclaw-plugin.yml"
- ".github/workflows/_release-npm-plugin.yml"
- ".github/workflows/release-openclaw-plugin.yml"
- "integrations/openclaw/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
package:
name: Validate OpenClaw plugin
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: integrations/openclaw
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24.16.0"
cache: npm
cache-dependency-path: integrations/openclaw/package-lock.json
- run: npm ci
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run test:package
- name: Validate ClawHub contract
run: npx --yes clawhub@0.23.3 package validate . --json

View file

@ -2,7 +2,7 @@ name: CI / Python packages
on:
push:
branches: [main, master, dev, develop]
branches: [main]
paths:
- '.github/workflows/ci-packages.yml'
- '.github/workflows/_build-python-packages.yml'
@ -14,7 +14,7 @@ on:
- 'tests/unit/test_package_versions.py'
- 'LICENSE'
pull_request:
branches: [main, master, dev, develop]
branches: [main]
paths:
- '.github/workflows/ci-packages.yml'
- '.github/workflows/_build-python-packages.yml'

View file

@ -2,7 +2,9 @@ name: CI / Python quality
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
@ -13,16 +15,39 @@ concurrency:
cancel-in-progress: true
jobs:
actionlint:
name: GitHub Actions
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Validate workflows with actionlint
env:
ACTIONLINT_VERSION: 1.7.12
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
run: |
archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
curl --fail --location --proto '=https' --retry 3 --silent --show-error \
--output "${RUNNER_TEMP}/${archive}" \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${archive}"
echo "${ACTIONLINT_SHA256} ${RUNNER_TEMP}/${archive}" | sha256sum --check
tar -xzf "${RUNNER_TEMP}/${archive}" -C "${RUNNER_TEMP}" actionlint
"${RUNNER_TEMP}/actionlint" .github/workflows/*.yml
pre-commit:
name: Pre-commit
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.11'
cache: pip

View file

@ -2,9 +2,9 @@ name: CI / Python tests
on:
push:
branches: [main, master, dev, develop]
branches: [main]
pull_request:
branches: [main, master, dev, develop]
branches: [main]
workflow_dispatch:
concurrency:
@ -18,18 +18,19 @@ jobs:
unit-tests:
name: Unit Tests - py${{ matrix.python-version }}
runs-on: ubuntu-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

View file

@ -2,6 +2,7 @@ name: CI / ReMe Studio
on:
push:
branches: [main]
paths:
- "reme_studio/**"
- ".github/workflows/ci-reme-studio.yml"
@ -11,6 +12,7 @@ on:
- "pyproject.toml"
- "LICENSE"
pull_request:
branches: [main]
paths:
- "reme_studio/**"
- ".github/workflows/ci-reme-studio.yml"
@ -33,17 +35,18 @@ jobs:
studio:
name: Studio checks
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: reme_studio
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22.22.3"
cache: npm
@ -67,7 +70,7 @@ jobs:
tar -tzf "${RUNNER_TEMP}"/agentscope-ai-reme_studio-*.tgz | grep '^package/dist-static/index.html$'
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.11"

View file

@ -1,62 +0,0 @@
name: CI / TypeScript plugins
on:
push:
branches: [main, master, dev, develop]
paths:
- ".github/workflows/ci-typescript.yml"
- ".github/workflows/release-typescript-plugin.yml"
- "integrations/dsh/**"
- "integrations/openclaw/**"
pull_request:
branches: [main, master, dev, develop]
paths:
- ".github/workflows/ci-typescript.yml"
- ".github/workflows/release-typescript-plugin.yml"
- "integrations/dsh/**"
- "integrations/openclaw/**"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
package:
name: Validate ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: DeepSeek Harness plugin
directory: integrations/dsh
- name: OpenClaw plugin
directory: integrations/openclaw
defaults:
run:
working-directory: ${{ matrix.directory }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: "24.16.0"
cache: npm
cache-dependency-path: ${{ matrix.directory }}/package-lock.json
- run: npm ci
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run test:package
- name: Validate OpenClaw package contract
if: matrix.directory == 'integrations/openclaw'
run: npx --yes clawhub@0.23.3 package validate . --json

View file

@ -2,9 +2,9 @@ name: CI / Windows
on:
push:
branches: [main, master, dev, develop]
branches: [main]
pull_request:
branches: [main, master, dev, develop]
branches: [main]
workflow_dispatch:
concurrency:
@ -18,18 +18,19 @@ jobs:
cli-smoke:
name: CLI smoke - py${{ matrix.python-version }}
runs-on: windows-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'

View file

@ -49,10 +49,11 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
timeout-minutes: 10
permissions:
pages: write
id-token: write
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1

View file

@ -2,8 +2,8 @@ name: Policy / PR title
on:
pull_request:
branches: [main, master, dev, develop]
types: [opened, edited, synchronize, reopened]
branches: [main]
types: [opened, edited, reopened]
permissions:
contents: read
@ -12,6 +12,7 @@ permissions:
jobs:
check-pr-title:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check PR title format
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1

View file

@ -30,16 +30,17 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
RELEASE_VERSION: ${{ inputs.version }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.11'
@ -149,7 +150,7 @@ jobs:
PY
- name: Upload distributions
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: reme-auto-fin-${{ inputs.version }}
path: dist/auto-fin/
@ -158,6 +159,7 @@ jobs:
publish:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
environment: pypi
permissions:
contents: read
@ -165,7 +167,7 @@ jobs:
steps:
- name: Download distributions
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: reme-auto-fin-${{ inputs.version }}
path: dist/auto-fin

View file

@ -29,16 +29,17 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
RELEASE_VERSION: ${{ inputs.version }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.11'
@ -149,7 +150,7 @@ jobs:
PY
- name: Upload distributions
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: reme-daily-paper-${{ inputs.version }}
path: dist/daily-paper/
@ -158,6 +159,7 @@ jobs:
publish:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
environment: pypi
permissions:
contents: read
@ -165,7 +167,7 @@ jobs:
steps:
- name: Download distributions
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: reme-daily-paper-${{ inputs.version }}
path: dist/daily-paper

View file

@ -0,0 +1,49 @@
# Configure npm Trusted Publishing for this caller filename and the npm environment.
name: Release / DSH plugin
run-name: Publish ReMe DSH plugin ${{ inputs.version }} (${{ inputs.npm_tag }})
on:
workflow_dispatch:
inputs:
version:
description: Exact package.json version; an optional v prefix is accepted
required: true
type: string
npm_tag:
description: npm distribution tag
required: true
default: latest
type: choice
options:
- next
- latest
use_npm_token:
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: publish-reme-dsh-plugin
cancel-in-progress: false
jobs:
release:
if: github.ref == 'refs/heads/main'
permissions:
contents: read
id-token: write
uses: ./.github/workflows/_release-npm-plugin.yml
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
with:
directory: integrations/dsh
package_name: '@agentscope-ai/reme-dsh-plugin'
artifact_name: agentscope-ai-reme-dsh-plugin
version: ${{ inputs.version }}
npm_tag: ${{ inputs.npm_tag }}
use_npm_token: ${{ inputs.use_npm_token }}

View file

@ -0,0 +1,75 @@
# Configure npm Trusted Publishing for this caller filename and the npm environment.
name: Release / OpenClaw plugin
run-name: Publish ReMe OpenClaw plugin ${{ inputs.version }} (${{ inputs.npm_tag }})
on:
workflow_dispatch:
inputs:
version:
description: Exact package.json version; an optional v prefix is accepted
required: true
type: string
npm_tag:
description: npm distribution tag
required: true
default: latest
type: choice
options:
- next
- latest
use_npm_token:
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
required: true
default: false
type: boolean
publish_clawhub:
description: Also publish the package to ClawHub
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: publish-reme-openclaw-plugin
cancel-in-progress: false
jobs:
release:
if: github.ref == 'refs/heads/main'
permissions:
contents: read
id-token: write
uses: ./.github/workflows/_release-npm-plugin.yml
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
with:
directory: integrations/openclaw
package_name: '@agentscope-ai/reme-openclaw-plugin'
artifact_name: agentscope-ai-reme-openclaw-plugin
version: ${{ inputs.version }}
npm_tag: ${{ inputs.npm_tag }}
use_npm_token: ${{ inputs.use_npm_token }}
validate_clawhub: true
publish-clawhub:
if: ${{ inputs.publish_clawhub && github.ref == 'refs/heads/main' }}
needs: release
permissions:
actions: read
contents: read
id-token: write
uses: openclaw/clawhub/.github/workflows/package-publish.yml@87ca030c30f3cfb78ab15c8e66b5ff1469c8f9c8 # v0.23.3
with:
family: code-plugin
version: ${{ needs.release.outputs.version }}
tags: ${{ inputs.npm_tag }}
source_repo: ${{ github.repository }}
source_commit: ${{ github.sha }}
source_ref: ${{ github.sha }}
source_path: integrations/openclaw
package_artifact_name: agentscope-ai-reme-openclaw-plugin-${{ needs.release.outputs.version }}
dry_run: false
wait_for_publication: true

View file

@ -1,14 +1,15 @@
name: Release / Python packages
name: Release / ReMe Python package
# GitHub Releases in this repository publish only the reme-ai Python package.
# Configure a PyPI Trusted Publisher for this repository, workflow, and its
# pypi environment before running an automatic or manual release.
# Publishing a GitHub Release is the primary release trigger for reme-ai; workflow_dispatch is the recovery path.
# Configure a PyPI Trusted Publisher for this repository, workflow, and its pypi environment first.
run-name: Publish reme-ai ${{ github.event.release.tag_name || inputs.version }}
on:
workflow_dispatch:
inputs:
version:
description: Release version
description: Exact reme-ai version; an optional v prefix is accepted
required: true
type: string
release:
@ -26,19 +27,20 @@ jobs:
name: Build and verify distributions
uses: ./.github/workflows/_build-python-packages.yml
with:
expected_version: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.version }}
expected_version: ${{ github.event.release.tag_name || inputs.version }}
upload_artifacts: true
publish-reme:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
environment: pypi
permissions:
contents: read
id-token: write
steps:
- name: Download ReMe distributions
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: reme-distributions
path: dist/reme

View file

@ -1,6 +1,6 @@
# Release checklist:
# 1. Update reme_studio/pyproject.toml, package.json, and package-lock.json to the same Studio version.
# 2. Configure npm Trusted Publishing and PyPI Trusted Publishing with the pypi environment.
# 2. Configure npm Trusted Publishing with the npm environment and PyPI Trusted Publishing with the pypi environment.
# 3. Run this workflow manually with the exact Studio version.
name: Release / ReMe Studio
@ -22,6 +22,20 @@ on:
options:
- next
- latest
use_npm_token:
description: Use the npm environment NPM_TOKEN instead of Trusted Publishing
required: true
default: false
type: boolean
publish_target:
description: Packages to publish; single-package modes are for release recovery
required: true
default: both
type: choice
options:
- both
- pypi
- npm
permissions:
contents: read
@ -32,23 +46,25 @@ concurrency:
jobs:
build:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 45
env:
RELEASE_VERSION: ${{ inputs.version }}
NPM_TAG: ${{ inputs.npm_tag }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22.22.3"
cache: npm
cache-dependency-path: reme_studio/package-lock.json
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.11"
@ -108,7 +124,7 @@ jobs:
assert (static_dir() / "index.html").is_file()
PY
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: reme-studio-${{ inputs.version }}
path: |
@ -117,14 +133,16 @@ jobs:
if-no-files-found: error
publish-python:
if: inputs.publish_target != 'npm'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
environment: pypi
permissions:
contents: read
id-token: write
steps:
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: reme-studio-${{ inputs.version }}
path: dist
@ -136,23 +154,90 @@ jobs:
skip-existing: true
publish-npm:
needs: build
if: >-
!cancelled() &&
needs.build.result == 'success' &&
(inputs.publish_target == 'npm' ||
(inputs.publish_target == 'both' && needs.publish-python.result == 'success'))
needs: [build, publish-python]
runs-on: ubuntu-latest
timeout-minutes: 10
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
registry-url: https://registry.npmjs.org
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: reme-studio-${{ inputs.version }}
path: dist
- name: Publish ReMe Studio to npm
- name: Verify the matching PyPI release for npm-only recovery
if: inputs.publish_target == 'npm'
env:
PACKAGE_VERSION: ${{ inputs.version }}
run: |
python - <<'PY'
import os
import urllib.error
import urllib.request
version = os.environ["PACKAGE_VERSION"].removeprefix("v")
url = f"https://pypi.org/pypi/reme-studio/{version}/json"
try:
with urllib.request.urlopen(url, timeout=30) as response:
if response.status != 200:
raise SystemExit(f"Unexpected PyPI response for reme-studio {version}: {response.status}")
except urllib.error.HTTPError as exc:
raise SystemExit(f"reme-studio {version} must exist on PyPI before npm-only recovery") from exc
PY
- name: Check for an identical existing npm package
id: npm-version
env:
PACKAGE_VERSION: ${{ inputs.version }}
run: |
package_file=$(find dist/studio-npm -maxdepth 1 -name '*.tgz' -print -quit)
if [[ -z "${package_file}" ]]; then
echo "Studio npm artifact is missing" >&2
exit 1
fi
local_integrity=$(node --input-type=module - "${package_file}" <<'JS'
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
const digest = createHash('sha512').update(readFileSync(process.argv[2])).digest('base64');
console.log(`sha512-${digest}`);
JS
)
if remote_integrity=$(npm view "@agentscope-ai/reme_studio@${PACKAGE_VERSION#v}" dist.integrity 2>/dev/null); then
if [[ "${remote_integrity}" != "${local_integrity}" ]]; then
echo "Existing npm package has different contents" >&2
exit 1
fi
echo "exists=true" >> "${GITHUB_OUTPUT}"
echo "The identical npm package already exists; nothing to publish"
else
echo "exists=false" >> "${GITHUB_OUTPUT}"
fi
- name: Publish ReMe Studio to npm with Trusted Publishing
if: ${{ steps.npm-version.outputs.exists != 'true' && !inputs.use_npm_token }}
env:
NPM_TAG: ${{ inputs.npm_tag }}
run: npm publish dist/studio-npm/*.tgz --access public --tag "${NPM_TAG}" --provenance
- name: Publish ReMe Studio to npm with NPM_TOKEN
if: ${{ steps.npm-version.outputs.exists != 'true' && inputs.use_npm_token }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TAG: ${{ inputs.npm_tag }}
run: |
if [[ -z "${NODE_AUTH_TOKEN}" ]]; then
echo "NPM_TOKEN is required when use_npm_token is enabled" >&2
exit 1
fi
npm publish dist/studio-npm/*.tgz --access public --tag "${NPM_TAG}" --provenance

View file

@ -1,172 +0,0 @@
# Publish one self-contained host plugin without coupling its version to the other host.
name: Release / TypeScript plugin
run-name: Publish ReMe ${{ inputs.plugin }} plugin ${{ inputs.version }} (${{ inputs.npm_tag }})
on:
workflow_dispatch:
inputs:
plugin:
description: Host plugin to publish
required: true
type: choice
options:
- dsh
- openclaw
version:
description: Exact package.json version; an optional v prefix is accepted
required: true
type: string
npm_tag:
description: npm distribution tag
required: true
default: latest
type: choice
options:
- next
- latest
publish_clawhub:
description: Also publish the OpenClaw package to ClawHub
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: publish-reme-${{ inputs.plugin }}-plugin
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact: ${{ steps.package.outputs.artifact }}
directory: ${{ steps.package.outputs.directory }}
name: ${{ steps.package.outputs.name }}
version: ${{ steps.validate.outputs.version }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Select package
id: package
env:
PLUGIN: ${{ inputs.plugin }}
run: |
case "$PLUGIN" in
dsh)
echo 'directory=integrations/dsh' >> "$GITHUB_OUTPUT"
echo 'name=@agentscope-ai/reme-dsh-plugin' >> "$GITHUB_OUTPUT"
echo 'artifact=agentscope-ai-reme-dsh-plugin' >> "$GITHUB_OUTPUT"
;;
openclaw)
echo 'directory=integrations/openclaw' >> "$GITHUB_OUTPUT"
echo 'name=@agentscope-ai/reme-openclaw-plugin' >> "$GITHUB_OUTPUT"
echo 'artifact=agentscope-ai-reme-openclaw-plugin' >> "$GITHUB_OUTPUT"
;;
*) exit 1 ;;
esac
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: "24.16.0"
cache: npm
cache-dependency-path: ${{ steps.package.outputs.directory }}/package-lock.json
- name: Validate package identity and version
id: validate
working-directory: ${{ steps.package.outputs.directory }}
env:
EXPECTED_NAME: ${{ steps.package.outputs.name }}
RELEASE_VERSION: ${{ inputs.version }}
NPM_TAG: ${{ inputs.npm_tag }}
run: |
node --input-type=module <<'JS'
import { appendFileSync, readFileSync } from 'node:fs';
const manifest = JSON.parse(readFileSync('package.json', 'utf8'));
const expected = process.env.RELEASE_VERSION.replace(/^v/, '');
if (manifest.name !== process.env.EXPECTED_NAME) throw new Error(`Unexpected package name: ${manifest.name}`);
if (manifest.version !== expected) throw new Error(`package.json is ${manifest.version}, workflow input is ${expected}`);
if (manifest.version.includes('-') !== (process.env.NPM_TAG === 'next')) {
throw new Error('Prereleases must use next; stable releases must use latest');
}
appendFileSync(process.env.GITHUB_OUTPUT, `version=${manifest.version}\n`);
JS
- run: npm ci
working-directory: ${{ steps.package.outputs.directory }}
- name: Validate package
working-directory: ${{ steps.package.outputs.directory }}
run: |
npm run format:check
npm run lint
npm run typecheck
npm test
npm run test:package
- name: Validate ClawHub contract
if: inputs.plugin == 'openclaw'
working-directory: integrations/openclaw
run: npx --yes clawhub@0.23.3 package validate . --json
- name: Pack
working-directory: ${{ steps.package.outputs.directory }}
run: |
mkdir -p "$RUNNER_TEMP/plugin-package"
npm pack --pack-destination "$RUNNER_TEMP/plugin-package"
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: ${{ steps.package.outputs.artifact }}-${{ steps.validate.outputs.version }}
path: ${{ runner.temp }}/plugin-package/*.tgz
if-no-files-found: error
publish:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: "24"
registry-url: https://registry.npmjs.org
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: ${{ needs.build.outputs.artifact }}-${{ needs.build.outputs.version }}
path: dist/plugin
- name: Reject an existing package version
env:
PACKAGE_NAME: ${{ needs.build.outputs.name }}
PACKAGE_VERSION: ${{ needs.build.outputs.version }}
run: |
if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${PACKAGE_VERSION} already exists" >&2
exit 1
fi
- name: Publish to npm
env:
NPM_TAG: ${{ inputs.npm_tag }}
run: npm publish dist/plugin/*.tgz --access public --tag "$NPM_TAG" --provenance
publish-clawhub:
if: ${{ inputs.plugin == 'openclaw' && inputs.publish_clawhub }}
needs: [build, publish]
permissions:
actions: read
contents: read
id-token: write
uses: openclaw/clawhub/.github/workflows/package-publish.yml@87ca030c30f3cfb78ab15c8e66b5ff1469c8f9c8 # v0.23.3
with:
family: code-plugin
version: ${{ needs.build.outputs.version }}
tags: ${{ inputs.npm_tag }}
source_repo: ${{ github.repository }}
source_commit: ${{ github.sha }}
source_ref: ${{ github.sha }}
source_path: integrations/openclaw
package_artifact_name: ${{ needs.build.outputs.artifact }}-${{ needs.build.outputs.version }}
dry_run: false
wait_for_publication: true

View file

@ -10,9 +10,7 @@ on:
workflow_dispatch:
permissions:
actions: read
contents: read
packages: read
security-events: write
concurrency:
@ -23,6 +21,7 @@ jobs:
analyze:
name: Analyze ${{ matrix.language }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
@ -30,7 +29,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

View file

@ -1345,19 +1345,24 @@ def test_auto_resource_handles_file_removed_before_stat():
_install_file_jobs(app_ctx, fs)
try:
source = write_file(cwd / "resource" / "2026-01-01" / "vanishing.txt", "content")
original_is_file = Path.is_file
original_stat = Path.stat
source_stat_calls = 0
def existing_is_file(path, *args, **kwargs):
if path == source:
return True
return original_is_file(path, *args, **kwargs)
def disappearing_stat(path, *args, **kwargs):
nonlocal source_stat_calls
if path == source:
source_stat_calls += 1
if source_stat_calls > 1:
raise FileNotFoundError("file disappeared")
raise FileNotFoundError("file disappeared")
return original_stat(path, *args, **kwargs)
step = AutoTextResourceStep(app_context=app_ctx, file_store=fs)
with patch.object(Path, "stat", disappearing_stat):
with (
patch.object(Path, "is_file", existing_is_file),
patch.object(Path, "stat", disappearing_stat),
):
resp = await step(
RuntimeContext(changes=[{"change": "added", "path": str(source)}]),
)