chore: import upstream snapshot with attribution
Automation / Format (push) Waiting to run
Automation / File Labeler (push) Waiting to run
Automation / Gemini Reviewed (push) Waiting to run
Coverage / Coverage (push) Waiting to run
Publish OpenClaw Skills / publish (push) Has been cancelled
Audit / Security Audit (push) Has been cancelled
Automation / Gemini Review (push) Has been cancelled
CI / Detect Changes (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Nix (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Cargo Deny (push) Has been cancelled
CI / Verify Skills (push) Has been cancelled
CI / Lint Skills (push) Has been cancelled
CI / Build (Linux x86_64) (push) Has been cancelled
CI / Build (macos-latest, aarch64-apple-darwin) (push) Has been cancelled
CI / Build (macos-latest, x86_64-apple-darwin) (push) Has been cancelled
CI / Build (ubuntu-latest, aarch64-unknown-linux-gnu) (push) Has been cancelled
CI / Build (windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
CI / API Smoketest (push) Has been cancelled
Policy / Policy Check (push) Has been cancelled
Release (Changeset) / Release (push) Has been cancelled
Automation / Format (push) Waiting to run
Automation / File Labeler (push) Waiting to run
Automation / Gemini Reviewed (push) Waiting to run
Coverage / Coverage (push) Waiting to run
Publish OpenClaw Skills / publish (push) Has been cancelled
Audit / Security Audit (push) Has been cancelled
Automation / Gemini Review (push) Has been cancelled
CI / Detect Changes (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Nix (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Cargo Deny (push) Has been cancelled
CI / Verify Skills (push) Has been cancelled
CI / Lint Skills (push) Has been cancelled
CI / Build (Linux x86_64) (push) Has been cancelled
CI / Build (macos-latest, aarch64-apple-darwin) (push) Has been cancelled
CI / Build (macos-latest, x86_64-apple-darwin) (push) Has been cancelled
CI / Build (ubuntu-latest, aarch64-unknown-linux-gnu) (push) Has been cancelled
CI / Build (windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
CI / API Smoketest (push) Has been cancelled
Policy / Policy Check (push) Has been cancelled
Release (Changeset) / Release (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Codeowners
|
||||
|
||||
# Core engine code strictly requires your review
|
||||
# Isolates agents to `skills/` or `src/helpers/` unless absolutely necessary
|
||||
/src/main.rs @jpoehnelt
|
||||
/src/executor.rs @jpoehnelt
|
||||
/src/discovery.rs @jpoehnelt
|
||||
/src/commands.rs @jpoehnelt
|
||||
/src/auth.rs @jpoehnelt
|
||||
/src/schema.rs @jpoehnelt
|
||||
@@ -0,0 +1,16 @@
|
||||
## Description
|
||||
|
||||
Please include a summary of the change and which issue is fixed. If adding a new feature or command, please include the output of running it with `--dry-run` to prove the JSON request body matches the Discovery Document schema.
|
||||
|
||||
**Dry Run Output:**
|
||||
```json
|
||||
// Paste --dry-run output here if applicable
|
||||
```
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] My code follows the `AGENTS.md` guidelines (no generated `google-*` crates).
|
||||
- [ ] I have run `cargo fmt --all` to format the code perfectly.
|
||||
- [ ] I have run `cargo clippy -- -D warnings` and resolved all warnings.
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works.
|
||||
- [ ] I have provided a Changeset file (e.g. via `pnpx changeset`) to document my changes.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Labels applied to PRs based on changed files.
|
||||
# Used by the actions/labeler action in .github/workflows/automation.yml
|
||||
|
||||
"area: auth":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/auth.rs
|
||||
- crates/google-workspace-cli/src/auth_commands.rs
|
||||
- crates/google-workspace-cli/src/setup.rs
|
||||
- crates/google-workspace-cli/src/accounts.rs
|
||||
- crates/google-workspace-cli/src/credential_store.rs
|
||||
- crates/google-workspace-cli/src/token_storage.rs
|
||||
- crates/google-workspace-cli/src/oauth_config.rs
|
||||
|
||||
"area: discovery":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/discovery.rs
|
||||
- crates/google-workspace-cli/src/services.rs
|
||||
- crates/google-workspace/src/discovery.rs
|
||||
- crates/google-workspace/src/services.rs
|
||||
|
||||
"area: http":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/executor.rs
|
||||
- crates/google-workspace-cli/src/client.rs
|
||||
- crates/google-workspace/src/client.rs
|
||||
|
||||
"area: tui":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/setup_tui.rs
|
||||
|
||||
"area: mcp":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/mcp_server.rs
|
||||
|
||||
"area: skills":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/generate_skills.rs
|
||||
- skills/**
|
||||
|
||||
"area: docs":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "*.md"
|
||||
- docs/**
|
||||
|
||||
"area: distribution":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- .github/workflows/release.yml
|
||||
- .github/workflows/release-changesets.yml
|
||||
- dist-workspace.toml
|
||||
- Cargo.toml
|
||||
- crates/*/Cargo.toml
|
||||
|
||||
"area: core":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace-cli/src/main.rs
|
||||
- crates/google-workspace-cli/src/commands.rs
|
||||
- crates/google-workspace-cli/src/error.rs
|
||||
- crates/google-workspace-cli/src/formatter.rs
|
||||
- crates/google-workspace-cli/src/fs_util.rs
|
||||
- crates/google-workspace-cli/src/helpers/**
|
||||
- crates/google-workspace-cli/src/text.rs
|
||||
- crates/google-workspace-cli/src/validate.rs
|
||||
- crates/google-workspace-cli/src/schema.rs
|
||||
|
||||
"crate: google-workspace":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/google-workspace/src/**
|
||||
- crates/google-workspace/Cargo.toml
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['Cargo.lock', 'Cargo.toml', 'crates/*/Cargo.toml']
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths: ['Cargo.lock', 'Cargo.toml', 'crates/*/Cargo.toml']
|
||||
schedule:
|
||||
- cron: '0 6 * * *' # Daily at 06:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
name: Security Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install cargo-audit
|
||||
uses: taiki-e/install-action@a37010ded18ff788be4440302bd6830b1ae50d8b # cargo-llvm-cov
|
||||
with:
|
||||
tool: cargo-audit
|
||||
|
||||
- name: Run cargo audit
|
||||
run: cargo audit
|
||||
@@ -0,0 +1,142 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Automation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
format:
|
||||
name: Format
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --all
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -A
|
||||
git diff --cached --quiet || git commit -m "style: cargo fmt" && git push
|
||||
|
||||
file-labeler:
|
||||
name: File Labeler
|
||||
if: github.event_name == 'pull_request_target'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
sync-labels: true
|
||||
|
||||
gemini-review:
|
||||
name: Gemini Review
|
||||
if: >-
|
||||
github.event_name == 'pull_request_target' &&
|
||||
github.event.action == 'synchronize'
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: gemini-review-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Remove reviewed label
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
name: 'gemini: reviewed',
|
||||
});
|
||||
} catch (e) {
|
||||
// Label not present — ignore
|
||||
}
|
||||
|
||||
- name: Debounce
|
||||
run: sleep 60
|
||||
|
||||
- name: Trigger Gemini Code Assist review
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: '/gemini review',
|
||||
});
|
||||
|
||||
gemini-reviewed:
|
||||
name: Gemini Reviewed
|
||||
if: >-
|
||||
github.event_name == 'pull_request_review' &&
|
||||
github.event.review.user.login == 'gemini-code-assist[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add reviewed label if review matches HEAD
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const pr = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
});
|
||||
|
||||
if (context.payload.review.commit_id !== pr.data.head.sha) {
|
||||
console.log(`Review is for ${context.payload.review.commit_id} but HEAD is ${pr.data.head.sha} — skipping label`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
labels: ['gemini: reviewed'],
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.status === 403) {
|
||||
console.log(`Token cannot add labels for this review event (${e.message}) — skipping`);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
SCCACHE_IGNORE_SERVER_IO_ERROR: "true"
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
name: Detect Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
rust: ${{ steps.filter.outputs.rust }}
|
||||
nix: ${{ steps.filter.outputs.nix }}
|
||||
skills: ${{ steps.filter.outputs.skills }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
rust:
|
||||
- '**/*.rs'
|
||||
- 'Cargo.toml'
|
||||
- 'crates/*/Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'build.rs'
|
||||
- '.cargo/**'
|
||||
nix:
|
||||
- 'flake.nix'
|
||||
- 'flake.lock'
|
||||
skills:
|
||||
- 'skills/**'
|
||||
|
||||
test:
|
||||
name: Test
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
|
||||
- name: Setup sccache
|
||||
id: sccache
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
continue-on-error: true
|
||||
|
||||
- name: Enable sccache
|
||||
if: steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
if sccache --start-server 2>/dev/null; then
|
||||
echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "::warning::sccache server failed to start, building without cache"
|
||||
fi
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2
|
||||
with:
|
||||
key: test-${{ matrix.os }}
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --workspace --verbose
|
||||
|
||||
nix:
|
||||
name: Nix
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rust == 'true' || needs.changes.outputs.nix == 'true' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@d96bc962e61b3049ce8128d03d57a1144fa96539 # main
|
||||
- name: Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@cec65ff6f104850203b152861d3f9e5f1747885d # main
|
||||
- name: Check flake
|
||||
run: nix flake check
|
||||
- name: Build flake
|
||||
run: nix build .
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Setup sccache
|
||||
id: sccache
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
continue-on-error: true
|
||||
|
||||
- name: Enable sccache
|
||||
if: steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
if sccache --start-server 2>/dev/null; then
|
||||
echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "::warning::sccache server failed to start, building without cache"
|
||||
fi
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2
|
||||
with:
|
||||
key: lint
|
||||
|
||||
- name: Check formatting
|
||||
run: |
|
||||
if ! cargo fmt --all -- --check; then
|
||||
echo "::error::Cargo fmt failed. Please run 'cargo fmt --all' locally and commit the changes."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy --workspace -- -D warnings
|
||||
|
||||
deny:
|
||||
name: Cargo Deny
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Cargo deny
|
||||
uses: EmbarkStudios/cargo-deny-action@3fd3802e88374d3fe9159b834c7714ec57d6c979 # v2.0.15
|
||||
with:
|
||||
command: check
|
||||
|
||||
|
||||
skills:
|
||||
name: Verify Skills
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
|
||||
- name: Setup sccache
|
||||
id: sccache
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
continue-on-error: true
|
||||
|
||||
- name: Enable sccache
|
||||
if: steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
if sccache --start-server 2>/dev/null; then
|
||||
echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "::warning::sccache server failed to start, building without cache"
|
||||
fi
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2
|
||||
with:
|
||||
key: skills
|
||||
|
||||
- name: Regenerate skills
|
||||
run: cargo run -- generate-skills --output-dir skills
|
||||
|
||||
- name: Check for drift
|
||||
run: |
|
||||
if ! git diff --exit-code skills/; then
|
||||
echo "::warning::Skills are out of date — the hourly auto-sync PR will fix this automatically."
|
||||
fi
|
||||
|
||||
lint-skills:
|
||||
name: Lint Skills
|
||||
needs: changes
|
||||
if: needs.changes.outputs.skills == 'true' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
||||
|
||||
- name: Validate skills
|
||||
run: |
|
||||
failed=0
|
||||
for skill_dir in skills/*/; do
|
||||
if ! uvx --from skills-ref@0.1.1 agentskills validate "$skill_dir"; then
|
||||
failed=1
|
||||
fi
|
||||
done
|
||||
if [ "$failed" -ne 0 ]; then
|
||||
echo "::error::One or more skills failed validation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
build-linux:
|
||||
name: Build (Linux x86_64)
|
||||
needs: changes
|
||||
if: needs.changes.outputs.rust == 'true' || github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
with:
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Setup sccache
|
||||
id: sccache
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
continue-on-error: true
|
||||
|
||||
- name: Enable sccache
|
||||
if: steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
if sccache --start-server 2>/dev/null; then
|
||||
echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "::warning::sccache server failed to start, building without cache"
|
||||
fi
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2
|
||||
with:
|
||||
key: build-x86_64-unknown-linux-gnu
|
||||
cache-targets: "false"
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --target x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Upload binary
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: gws-linux-x86_64
|
||||
path: target/x86_64-unknown-linux-gnu/release/gws
|
||||
retention-days: 1
|
||||
|
||||
build:
|
||||
name: Build
|
||||
needs: [smoketest, changes]
|
||||
if: |
|
||||
always() && !cancelled() && !failure()
|
||||
&& (needs.changes.outputs.rust == 'true' || github.event_name == 'push')
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Setup sccache
|
||||
id: sccache
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
continue-on-error: true
|
||||
|
||||
- name: Enable sccache
|
||||
if: steps.sccache.outcome == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
if sccache --start-server 2>/dev/null; then
|
||||
echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "::warning::sccache server failed to start, building without cache"
|
||||
fi
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2
|
||||
with:
|
||||
key: build-${{ matrix.target }}
|
||||
cache-targets: "false"
|
||||
|
||||
- name: Install cross-compilation tools
|
||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||
|
||||
- name: Disable Windows Defender scanning for cargo
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
Add-MpPreference -ExclusionPath "$env:USERPROFILE\.cargo"
|
||||
Add-MpPreference -ExclusionPath "$env:USERPROFILE\.rustup"
|
||||
Add-MpPreference -ExclusionPath "${{ github.workspace }}\target"
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --target ${{ matrix.target }}
|
||||
env:
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
||||
|
||||
smoketest:
|
||||
name: API Smoketest
|
||||
needs: build-linux
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Download binary
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: gws-linux-x86_64
|
||||
path: ./bin
|
||||
|
||||
- name: Make binary executable
|
||||
run: chmod +x ./bin/gws
|
||||
|
||||
- name: Decode credentials
|
||||
env:
|
||||
GOOGLE_CREDENTIALS_JSON: ${{ secrets.GOOGLE_CREDENTIALS_JSON }}
|
||||
run: |
|
||||
if [ -z "$GOOGLE_CREDENTIALS_JSON" ]; then
|
||||
echo "::error::GOOGLE_CREDENTIALS_JSON secret is not set"
|
||||
exit 1
|
||||
fi
|
||||
echo "$GOOGLE_CREDENTIALS_JSON" | base64 -d > /tmp/credentials.json
|
||||
|
||||
- name: Smoketest — help
|
||||
run: ./bin/gws --help
|
||||
|
||||
- name: Smoketest — schema introspection
|
||||
run: ./bin/gws schema drive.files.list | jq -e '.httpMethod'
|
||||
|
||||
- name: Smoketest — Drive files list
|
||||
env:
|
||||
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE: /tmp/credentials.json
|
||||
run: |
|
||||
./bin/gws drive files list \
|
||||
--params '{"pageSize": 1, "fields": "files(id,mimeType)"}' \
|
||||
| jq -e '.files'
|
||||
|
||||
- name: Smoketest — Gmail messages
|
||||
env:
|
||||
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE: /tmp/credentials.json
|
||||
run: |
|
||||
./bin/gws gmail users messages list \
|
||||
--params '{"userId": "me", "maxResults": 1, "fields": "messages(id)"}' \
|
||||
| jq -e '.messages'
|
||||
|
||||
- name: Smoketest — Calendar events
|
||||
env:
|
||||
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE: /tmp/credentials.json
|
||||
run: |
|
||||
./bin/gws calendar events list \
|
||||
--params '{"calendarId": "primary", "maxResults": 1, "fields": "kind,items(id,status)"}' \
|
||||
| jq -e '.kind'
|
||||
|
||||
- name: Smoketest — Slides presentation
|
||||
env:
|
||||
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE: /tmp/credentials.json
|
||||
run: |
|
||||
./bin/gws slides presentations get \
|
||||
--params '{"presentationId": "1knOKD_87JWE4qsEbO4r5O91IxTER5ybBBhOJgZ1yLFI", "fields": "presentationId,slides(objectId)"}' \
|
||||
| jq -e '.presentationId'
|
||||
|
||||
- name: Smoketest — pagination
|
||||
env:
|
||||
GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE: /tmp/credentials.json
|
||||
run: |
|
||||
LINES=$(./bin/gws drive files list \
|
||||
--params '{"pageSize": 1, "fields": "nextPageToken,files(id)"}' \
|
||||
--page-all --page-limit 2 \
|
||||
| wc -l)
|
||||
if [ "$LINES" -lt 2 ]; then
|
||||
echo "::error::Expected at least 2 NDJSON lines from pagination, got $LINES"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Smoketest — error handling
|
||||
run: |
|
||||
if ./bin/gws fakeservice list 2>&1; then
|
||||
echo "::error::Expected exit code 1 for unknown service"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Cleanup credentials
|
||||
if: always()
|
||||
run: rm -f /tmp/credentials.json
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: CLA
|
||||
|
||||
on:
|
||||
check_run:
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
cla-label:
|
||||
name: CLA Label
|
||||
if: github.event.check_run.name == 'cla/google'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update CLA label
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||||
with:
|
||||
github-token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
script: |
|
||||
const cr = context.payload.check_run;
|
||||
const passed = cr.conclusion === 'success';
|
||||
|
||||
for (const pr of cr.pull_requests) {
|
||||
const labels = passed
|
||||
? { add: 'cla: yes', remove: 'cla: no' }
|
||||
: { add: 'cla: no', remove: 'cla: yes' };
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
labels: [labels.add],
|
||||
});
|
||||
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
name: labels.remove,
|
||||
});
|
||||
} catch (e) {
|
||||
// Label not present — ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Coverage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['**/*.rs', 'Cargo.toml', 'Cargo.lock']
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths: ['**/*.rs', 'Cargo.toml', 'Cargo.lock']
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
with:
|
||||
components: llvm-tools-preview
|
||||
|
||||
- name: Install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@a37010ded18ff788be4440302bd6830b1ae50d8b # cargo-llvm-cov
|
||||
with:
|
||||
tool: cargo-llvm-cov
|
||||
|
||||
- name: Generate code coverage
|
||||
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
|
||||
with:
|
||||
files: lcov.info
|
||||
fail_ci_if_error: false
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -0,0 +1,119 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Generate Skills
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *" # Hourly — keeps skills in sync with Discovery API changes
|
||||
workflow_dispatch: # Manual trigger
|
||||
push:
|
||||
branches-ignore:
|
||||
- main # main is kept up to date by PR merges
|
||||
|
||||
concurrency:
|
||||
group: generate-skills-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
name: Generate and commit skills
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
# For cron/dispatch: check out main. For push: check out the branch.
|
||||
ref: ${{ github.head_ref || github.ref_name }}
|
||||
token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
|
||||
- name: Setup sccache
|
||||
uses: mozilla-actions/sccache-action@2df7dbab909c49ab7d3382d05da469f3f975c2d6 # v0.0.7
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@ad397744b0d591a723ab90405b7247fac0e6b8db # v2
|
||||
with:
|
||||
key: generate-skills-ubuntu
|
||||
|
||||
- name: Generate skills
|
||||
run: cargo run -- generate-skills
|
||||
|
||||
- name: Check for changes
|
||||
id: diff
|
||||
run: |
|
||||
if git diff --quiet skills/ docs/skills.md; then
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# --- Cron / workflow_dispatch: open a PR against main ---
|
||||
- name: Create changeset for sync PR
|
||||
if: >-
|
||||
steps.diff.outputs.changed == 'true' &&
|
||||
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
run: |
|
||||
mkdir -p .changeset
|
||||
cat > .changeset/sync-skills.md << 'EOF'
|
||||
---
|
||||
"@googleworkspace/cli": patch
|
||||
---
|
||||
|
||||
Sync generated skills with latest Google Discovery API specs
|
||||
EOF
|
||||
|
||||
- name: Create or update sync PR
|
||||
if: >-
|
||||
steps.diff.outputs.changed == 'true' &&
|
||||
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
|
||||
with:
|
||||
token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
branch: chore/sync-skills
|
||||
title: "chore: sync skills with Discovery API"
|
||||
body: |
|
||||
Automated PR — the Google Discovery API specs have changed and the
|
||||
generated skill files are out of date.
|
||||
|
||||
Created by the **Generate Skills** workflow (`generate-skills.yml`).
|
||||
commit-message: "chore: regenerate skills from Discovery API"
|
||||
add-paths: |
|
||||
skills/
|
||||
docs/skills.md
|
||||
.changeset/sync-skills.md
|
||||
delete-branch: true
|
||||
|
||||
# --- Push events (non-main branches): commit directly ---
|
||||
- name: Commit and push if changed
|
||||
if: >-
|
||||
steps.diff.outputs.changed == 'true' &&
|
||||
github.event_name == 'push'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
run: |
|
||||
git config user.name "googleworkspace-bot"
|
||||
git config user.email "googleworkspace-bot@users.noreply.github.com"
|
||||
|
||||
git add skills/ docs/skills.md
|
||||
git commit -m "chore: regenerate skills [skip ci]"
|
||||
git push
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Policy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
policy-check:
|
||||
name: Policy Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Enforce AGENTS.md rules
|
||||
run: |
|
||||
# Check CLI crate for prohibited google-* registry crates.
|
||||
# Path dependencies (e.g. google-workspace = { path = ... }) are allowed.
|
||||
if grep -E "^google-[a-zA-Z0-9_-]+[[:space:]]*=" crates/google-workspace-cli/Cargo.toml | grep -v 'path[[:space:]]*='; then
|
||||
echo "::error file=crates/google-workspace-cli/Cargo.toml::Violates AGENTS.md: Adding generated google-* crates is prohibited. The CLI uses dynamic schema discovery at runtime."
|
||||
exit 1
|
||||
fi
|
||||
echo "Policy check passed."
|
||||
- name: Enforce Changeset File
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
git fetch origin ${{ github.base_ref }}
|
||||
# Skip changeset requirement if no Rust/Cargo files changed
|
||||
if ! git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -qE '\.(rs)$|^Cargo\.(toml|lock)$'; then
|
||||
echo "No Rust/Cargo files changed; skipping changeset requirement."
|
||||
exit 0
|
||||
fi
|
||||
if ! git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -q "^.changeset/.*\.md$"; then
|
||||
echo "::error::A Changeset file is required! Please run 'npx changeset' or manually create a markdown file in the .changeset directory describing your changes to automatically version and release this PR."
|
||||
exit 1
|
||||
fi
|
||||
echo "Changeset file found!"
|
||||
- name: Validate Changeset Package Name
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
for f in $(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep "^.changeset/.*\.md$"); do
|
||||
if grep -q '"googleworkspace-cli"' "$f"; then
|
||||
echo "::error file=$f::Wrong package name. Use '\"@googleworkspace/cli\"' not '\"googleworkspace-cli\"'."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "Changeset package names valid!"
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Publish OpenClaw Skills
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "skills/**"
|
||||
- ".github/workflows/publish-skills.yml"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "skills/**"
|
||||
- ".github/workflows/publish-skills.yml"
|
||||
schedule:
|
||||
- cron: "0 * * * *" # Hourly, to drip-publish past rate limits
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
# Skip fork PRs — secrets (CLAWHUB_TOKEN) are not available
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Install ClawHub CLI
|
||||
run: npm i -g clawhub@0.7.0
|
||||
|
||||
- name: Authenticate ClawHub
|
||||
env:
|
||||
CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
|
||||
run: |
|
||||
if [ -z "$CLAWHUB_TOKEN" ]; then
|
||||
echo "::error::CLAWHUB_TOKEN secret is not set"
|
||||
exit 1
|
||||
fi
|
||||
clawhub login --token "$CLAWHUB_TOKEN"
|
||||
|
||||
- name: Publish skills
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
clawhub sync --root skills --all --dry-run
|
||||
else
|
||||
clawhub sync --root skills --all
|
||||
fi
|
||||
@@ -0,0 +1,72 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Release (Changeset)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
|
||||
with:
|
||||
github_access_token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
|
||||
- uses: pnpm/action-setup@c5ba7f7862a0f64c1b1a05fbac13e0b8e86ba08c # v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- run: |
|
||||
git config --global user.name "googleworkspace-bot"
|
||||
git config --global user.email "googleworkspace-bot@google.com"
|
||||
|
||||
- name: Create Release Pull Request or Tag
|
||||
id: changesets
|
||||
uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf # v1
|
||||
with:
|
||||
version: pnpm run version-sync
|
||||
publish: pnpm run tag-release
|
||||
commit: 'chore: release versions'
|
||||
title: 'chore: release versions'
|
||||
setupGitUser: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }}
|
||||
@@ -0,0 +1,227 @@
|
||||
# Release workflow for @googleworkspace/cli
|
||||
#
|
||||
# Triggered by pushing a semver tag (e.g. v0.22.3).
|
||||
# Builds platform binaries, creates a GitHub Release, publishes to npm and crates.io.
|
||||
|
||||
name: Release
|
||||
permissions:
|
||||
contents: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+*'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
plan:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.meta.outputs.version }}
|
||||
prerelease: ${{ steps.meta.outputs.prerelease }}
|
||||
publishing: ${{ github.ref_type == 'tag' }}
|
||||
steps:
|
||||
- id: meta
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
VERSION="${TAG#v}"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$VERSION" == *-* ]]; then
|
||||
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build:
|
||||
needs: plan
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-apple-darwin
|
||||
runner: macos-latest
|
||||
archive: tar.gz
|
||||
- target: x86_64-apple-darwin
|
||||
runner: macos-latest
|
||||
archive: tar.gz
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
runner: ubuntu-latest
|
||||
archive: tar.gz
|
||||
cross: true
|
||||
- target: aarch64-unknown-linux-musl
|
||||
runner: ubuntu-latest
|
||||
archive: tar.gz
|
||||
cross: true
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
runner: ubuntu-latest
|
||||
archive: tar.gz
|
||||
- target: x86_64-unknown-linux-musl
|
||||
runner: ubuntu-latest
|
||||
archive: tar.gz
|
||||
cross: true
|
||||
- target: x86_64-pc-windows-msvc
|
||||
runner: windows-latest
|
||||
archive: zip
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Install cross
|
||||
if: matrix.cross
|
||||
run: cargo install cross --git https://github.com/cross-rs/cross --tag v0.2.5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
if [ "${{ matrix.cross }}" = "true" ]; then
|
||||
cross build --release --target ${{ matrix.target }} --locked
|
||||
else
|
||||
cargo build --release --target ${{ matrix.target }} --locked
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Package (unix)
|
||||
if: matrix.archive == 'tar.gz'
|
||||
run: |
|
||||
ARTIFACT="google-workspace-cli-${{ matrix.target }}"
|
||||
mkdir -p staging
|
||||
cp "target/${{ matrix.target }}/release/gws" staging/
|
||||
cp LICENSE README.md CHANGELOG.md staging/
|
||||
tar czf "${ARTIFACT}.tar.gz" -C staging .
|
||||
shasum -a 256 "${ARTIFACT}.tar.gz" > "${ARTIFACT}.tar.gz.sha256"
|
||||
shell: bash
|
||||
|
||||
- name: Package (windows)
|
||||
if: matrix.archive == 'zip'
|
||||
run: |
|
||||
$ARTIFACT = "google-workspace-cli-${{ matrix.target }}"
|
||||
New-Item -ItemType Directory -Path $ARTIFACT
|
||||
Copy-Item "target/${{ matrix.target }}/release/gws.exe" "$ARTIFACT/"
|
||||
Copy-Item LICENSE, README.md, CHANGELOG.md "$ARTIFACT/"
|
||||
Compress-Archive -Path "$ARTIFACT/*" -DestinationPath "$ARTIFACT.zip"
|
||||
(Get-FileHash "$ARTIFACT.zip" -Algorithm SHA256).Hash.ToLower() + " $ARTIFACT.zip" | Out-File -Encoding ascii "$ARTIFACT.zip.sha256"
|
||||
shell: pwsh
|
||||
|
||||
- name: Attest
|
||||
if: github.ref_type == 'tag'
|
||||
uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 # v3
|
||||
with:
|
||||
subject-path: google-workspace-cli-${{ matrix.target }}.${{ matrix.archive }}
|
||||
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: binary-${{ matrix.target }}
|
||||
path: |
|
||||
google-workspace-cli-${{ matrix.target }}.${{ matrix.archive }}
|
||||
google-workspace-cli-${{ matrix.target }}.${{ matrix.archive }}.sha256
|
||||
|
||||
release:
|
||||
needs: [plan, build]
|
||||
if: github.ref_type == 'tag'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: binary-*
|
||||
path: artifacts/
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PRERELEASE_FLAG=""
|
||||
if [ "${{ needs.plan.outputs.prerelease }}" = "true" ]; then
|
||||
PRERELEASE_FLAG="--prerelease"
|
||||
fi
|
||||
|
||||
NOTES="## Installation
|
||||
|
||||
Download the archive for your OS and architecture from the assets below.
|
||||
|
||||
### Linux / macOS
|
||||
|
||||
Replace \`<target>\` with your platform (e.g., \`aarch64-apple-darwin\` or \`x86_64-unknown-linux-gnu\`).
|
||||
|
||||
\`\`\`bash
|
||||
# 1. Download the archive and its checksum
|
||||
curl -sLO https://github.com/googleworkspace/cli/releases/download/${{ github.ref_name }}/google-workspace-cli-<target>.tar.gz
|
||||
curl -sLO https://github.com/googleworkspace/cli/releases/download/${{ github.ref_name }}/google-workspace-cli-<target>.tar.gz.sha256
|
||||
|
||||
# 2. Verify the checksum
|
||||
shasum -a 256 -c google-workspace-cli-<target>.tar.gz.sha256
|
||||
|
||||
# 3. Extract and install
|
||||
tar -xzf google-workspace-cli-<target>.tar.gz
|
||||
chmod +x gws
|
||||
sudo mv gws /usr/local/bin/
|
||||
\`\`\`
|
||||
|
||||
### Windows
|
||||
|
||||
1. Download \`google-workspace-cli-x86_64-pc-windows-msvc.zip\` and its \`.sha256\` file
|
||||
2. Verify the checksum (e.g., using PowerShell \`Get-FileHash\`)
|
||||
3. Extract the archive and move \`gws.exe\` to a directory included in your system \`PATH\`.
|
||||
|
||||
---
|
||||
"
|
||||
|
||||
gh release create "${{ github.ref_name }}" \
|
||||
--target "${{ github.sha }}" \
|
||||
--title "${{ github.ref_name }}" \
|
||||
--notes "$NOTES" \
|
||||
--generate-notes \
|
||||
$PRERELEASE_FLAG \
|
||||
artifacts/*
|
||||
|
||||
publish-npm:
|
||||
needs: [plan, release]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.plan.outputs.publishing == 'true' && needs.plan.outputs.prerelease == 'false' }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
|
||||
with:
|
||||
node-version: '20.x'
|
||||
registry-url: 'https://wombat-dressing-room.appspot.com'
|
||||
|
||||
- name: Publish to npm
|
||||
working-directory: npm
|
||||
run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
publish-cargo:
|
||||
needs: [plan, release]
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.plan.outputs.publishing == 'true' && needs.plan.outputs.prerelease == 'false' }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
||||
|
||||
# Publish library crate first (CLI depends on it)
|
||||
- name: Publish google-workspace
|
||||
run: cargo publish --package google-workspace
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
||||
# Wait for crates.io to index the library crate
|
||||
- name: Wait for crates.io index
|
||||
run: sleep 30
|
||||
|
||||
- name: Publish google-workspace-cli
|
||||
run: cargo publish --package google-workspace-cli
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: 'Close Stale PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
|
||||
with:
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
days-before-pr-stale: 3
|
||||
days-before-pr-close: 0
|
||||
stale-pr-message: 'This PR has been inactive for 72 hours. Closing to keep the queue clean.'
|
||||
close-pr-message: 'This PR was closed because it has been stalled for 72 hours. Feel free to magically reopen it if you want to continue working on it!'
|
||||
exempt-pr-labels: 'keep-alive'
|
||||
Reference in New Issue
Block a user