Skip to content

feat: kbc→kbagent CI/CD migration skill + sync CLI behavior tests - #402

Draft
Matovidlo wants to merge 12 commits into
mainfrom
martinvasko-kbc-to-kbagent-cicd-migration
Draft

feat: kbc→kbagent CI/CD migration skill + sync CLI behavior tests#402
Matovidlo wants to merge 12 commits into
mainfrom
martinvasko-kbc-to-kbagent-cicd-migration

Conversation

@Matovidlo

@Matovidlo Matovidlo commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds the kbagent-cicd-migration skill (plugins/kbagent/skills/kbagent-cicd-migration/): a guided, evidence-based runbook plus a stdlib-only generator (scripts/migrate_cicd.py) that migrates a kbc (keboola-as-code) project-as-code GitHub CI/CD pipeline to the new kbagent sync engine.
    • The generator discovers every project from .keboola/manifest.json, detects the legacy kbc workflows/actions it supersedes, and emits clean kbagent-native validate / pull / push GitHub workflows using uv tool install + kbagent sync with the KBAGENT_PROJECT_FROM_ENV=1 auth model (per-project token secrets, GitHub-Environment approval gating, no committed tokens).
    • References cover the kbc↔kbagent command/flag/env mapping, GitHub secrets/environments setup, the one-time breaking-conversion runbook, and the single-branch vs git-branching decision guide.
  • Adds sync CLI behavior tests (tests/test_sync_cli_behavior.py): pin the project-selection guards that define kbagent's orchestrator model (--project/--all-projects required and mutually exclusive, --branch is per-project) and that push --dry-run propagates the dry-run flag without writing.

Why

Customers run kbc-based GitHub CI/CD (per-project pull/push PRs, multi-project promotion). The new CLI's sync is a manifest-compatible successor but not a drop-in: the on-disk format differs (config.json/meta.json_config.yml) and sync is an orchestrator over registered project aliases, not cwd-per-folder. This skill gives a safe, repeatable cutover, and the tests lock the guard behaviors that prevent wrong-target or whole-tree operations.

Validated live against a real project: native pull round-trips to zero drift; the adopt-existing → diff "136 to delete" footgun and the orphaned-config.json cleanup are documented in the runbook.

Depends on #561 (kbagent auth pat-create/pat-revoke, v0.81.0): after a follow-up discussion, the skill now recommends minting a scoped Personal Access Token for each project's KBC_TOKEN_<ALIAS> CI secret instead of pasting a raw Storage API token. No generated-YAML change — a kbc_pat_... value is a drop-in for the existing KBAGENT_PROJECT_FROM_ENV=1 model, so this is purely a change in how the operator obtains the secret's value (a raw Storage token remains documented as the fallback).

Change type

Feature — migration tooling + tests. No source/CLI-command changes, no version bump.

Impact analysis

  • New files only: one skill tree under plugins/kbagent/skills/ and one test file under tests/.
  • No changes to src/, no new CLI commands, no public API or behavior change.
  • Fully backwards-compatible.

Test plan

  • tests/test_sync_cli_behavior.py — 6 tests, green.
  • Local gates pass: ruff lint + format, ty (no new diagnostics), command-sync-check (229 commands, none added), skill-check (main SKILL.md unchanged), changelog-check.
  • Generator exercised against the CLI-based-sync-demo repo (2 projects discovered, 3 legacy actions flagged, 3 valid workflows generated).

Deployment

Merge & automatic deploy. No migration.

Rollback plan

Revert of this PR.

@Matovidlo

Copy link
Copy Markdown
Contributor Author

@claude review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new kbagent CI/CD migration skill (docs + a stdlib-only workflow generator) intended to help customers migrate legacy kbc GitHub Actions pipelines to kbagent sync, and it adds behavioral CLI tests to lock down key sync safety guards (explicit project selection and dry-run propagation).

Changes:

  • Added kbagent-cicd-migration skill documentation + reference materials under plugins/kbagent/skills/.
  • Added scripts/migrate_cicd.py generator to detect legacy kbc workflows and emit kbagent-native validate/pull/push workflows.
  • Added tests/test_sync_cli_behavior.py to pin CLI guard behavior for project selection and push --dry-run.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
tests/test_sync_cli_behavior.py Adds CLI behavior regression tests for project selection guards and dry-run propagation.
plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md Migration skill runbook explaining the breaking JSON→YAML conversion and CI/CD cutover steps.
plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Analyzer + generator that discovers projects and emits GitHub Actions workflows + secrets checklist.
plugins/kbagent/skills/kbagent-cicd-migration/references/secrets-setup.md Documents GitHub secrets/environments setup for the generated workflows.
plugins/kbagent/skills/kbagent-cicd-migration/references/migration-runbook.md Ordered PR sequence / operational runbook for a safe migration.
plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md Reference mapping between kbc and kbagent commands/flags/env vars.
plugins/kbagent/skills/kbagent-cicd-migration/references/branching-model.md Decision guide for single-branch vs git-branching workflow models.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Outdated
Comment thread plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md Outdated
Matovidlo and others added 4 commits August 6, 2026 12:15
…ry-run safety

Pin the behaviors that make sync safe and that distinguish kbagent's
orchestrator model from kbc's cwd-per-folder model: sync pull/diff/push
require --project or --all-projects (and the two are mutually exclusive),
--branch is per-project, and push --dry-run propagates the dry-run flag
without writing. 6 tests via CliRunner with a mocked SyncService.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the kbagent-cicd-migration skill: a guided, evidence-based runbook plus
a stdlib-only generator (scripts/migrate_cicd.py) that discovers projects from
.keboola/manifest.json, detects the legacy kbc CI it replaces, and emits clean
kbagent-native validate/pull/push GitHub workflows using uv tool install +
kbagent sync with the KBAGENT_PROJECT_FROM_ENV auth model.

References cover the kbc<->kbagent command/flag/env mapping, GitHub
secrets/environments setup, the one-time breaking-conversion runbook (verified
against a live project: the adopt-existing 136-delete footgun and the orphaned
config.json cleanup), and the single-branch vs git-branching decision guide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran the recommended migration path end-to-end against project 153 (fresh
kbc init pull + kbagent v0.80.0) to verify the skill's own claims:

- migrate_cicd.py generated a `push` workflow using a nonexistent
  `--allow-delete` flag; kbagent's actual flag is `--force`. Fixed the
  generator and the command-mapping table.
- Plain `sync init` (the recommended no-adopt-existing mechanic) cannot
  run verbatim against a directory straight out of `kbc pull` -- kbc and
  kbagent share the same manifest path (`.keboola/manifest.json`), so it
  fails fast with "Manifest already exists" until that one file (not the
  config.json/meta.json tree) is deleted first. Folded the missing `rm`
  step into SKILL.md and migration-runbook.md.
- The "delete now-empty kbc-only type folders" cleanup step was a no-op:
  app/processor/_shared still hold description.md + code bodies kbagent
  never reads, so `find -empty -delete` matches nothing. Replaced with a
  full subtree removal, confirmed to leave `sync diff`/`sync status`
  unaffected.
- Re-confirmed the `--adopt-existing` phantom-rows bug reproduces exactly
  as documented (9 added / 1 deleted) on a side-by-side copy of the same
  tree, validating the plain-init recommendation.
- Added a Prerequisites section (repo path, kbagent install, per-project
  storage host+token, which project first) that the skill previously
  assumed rather than stated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ponytail pass over the skill: SKILL.md had accumulated three
chronological layers of investigation history (2026-06, then two
2026-08 rounds) that each superseded the last without removing it, plus
a full duplicate of migration-runbook.md's per-project conversion
procedure and its git-history-rewrite warning. Collapsed to a single
current-state finding and pointed Step 3b at the runbook instead of
re-deriving it -- same information, 383 -> 262 lines.

Also clarified the Prerequisites auth bullet: the generated CI
workflows always need a static per-project Storage API token
(`kbagent auth login` is browser-based and can't run unattended in
CI), but the local/interactive Step 3b conversion can use an already
browser-authenticated + registered project alias instead of a raw
token, skipping the KBAGENT_PROJECT_FROM_ENV env-injection entirely.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Matovidlo
Matovidlo force-pushed the martinvasko-kbc-to-kbagent-cicd-migration branch from 88a84da to ee02e27 Compare August 6, 2026 10:18
… layout

- Drop the `sync init --adopt-existing ... || true` step from every generated
  CI run: the manifest is already committed post-conversion and checked out
  by actions/checkout, so init was both redundant and silently swallowing
  real failures (wrong token/project mismatch) via `|| true`.
- Fix `--json` placement: it's a global option, not a per-subcommand flag
  (`kbagent --json sync diff ...`, not `sync diff --json`).
- Fix wrong pull flag names in command-mapping.md (--no-storage/--no-jobs/
  --with-samples, not --skip-storage/--skip-jobs/--with-table-samples).
- Add the missing required --project to the sync init mapping row.
- Remove the unused --git-branching flag from the generator (it never
  affected generation; git-branching is a per-project runtime choice made
  via `sync init --git-branching` + `branch-link`, documented in
  branching-model.md).
- Fix the Step 6 "tiny diff" claim, which contradicted the reality-check's
  "expect a massive reformatting diff" -- clarify it's checking for drift
  since the conversion commit, not comparing against the original kbc tree.
- De-duplicate the branching-model decision table (was in both
  migration-runbook.md and branching-model.md).
- Explicitly document that per-project directory layout (project-id-named,
  L0/L1-labeled, or flat single-project) is preserved verbatim -- the
  generator never renames or reorganizes it, and --all-projects must not be
  used for exactly this reason.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (6)

plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py:321

  • _guess_repo_slug fails to parse repo names containing dots because the regex excludes . in the repo segment, which can produce an incorrect --repo <owner>/<repo> in the generated gh commands. Capture the full slug and strip an optional .git suffix instead.
    if config.exists():
        m = re.search(r"github\.com[:/]([^/]+/[^/\s.]+)", config.read_text(errors="ignore"))
        if m:
            return m.group(1)

plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py:104

  • discover_projects accepts manifests with missing project.id / project.apiHost, which later yields invalid workflow env (e.g. KBC_STORAGE_API_URL: https://) and misleading secrets output. Treat these fields as required and skip or error on invalid manifests.
        proj = data.get("project", {})
        projects.append(
            Project(
                alias=_alias_from_dir(rel),
                directory=rel,

plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md:33

  • The code reference constants.py:425 is incorrect (it currently points at unrelated OTLP constants), which undermines the “verified against the code” claim. Prefer stable file/symbol references (or just file paths) instead of brittle line numbers.
   - `kbagent` writes per config: **`_config.yml`** (YAML, with `name`/`description`/
     `parameters` hoisted + a `_configuration_extra` block) + extracted code files
     (`constants.py:425`, `sync/config_format.py`).
   - The first `kbagent sync pull` therefore **rewrites every configuration** into a

plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md:34

  • The file/line references here are stale: constants.py:163 and config_store.py:193 don’t correspond to the env-injection mechanism. Use stable symbol/file references (e.g., constants.py ENV_PROJECT_FROM_ENV and ConfigStore._inject_env_project) rather than line numbers.
| *(implicit)* | `KBAGENT_PROJECT_FROM_ENV=1` | **Required** opt-in so kbagent synthesizes an ephemeral project from the env in CI (no `config.json` on disk). See `constants.py:163`, `config_store.py:193` |

plugins/kbagent/skills/kbagent-cicd-migration/references/secrets-setup.md:50

  • The .gitignore reference is incorrect: config_store.py:359 is unrelated to gitignore creation. Point to the actual helper (or drop the line number) so readers can verify the claim reliably.
- Do **not** commit `.kbagent/config.json` with tokens (the new CLI auto-writes a
  `.gitignore` for its config dir — `config_store.py:359`).
- Do **not** pass `--allow-plaintext-on-encrypt-failure` in CI.

plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md:40

  • The parenthetical reference sync.py:67,495 appears to be stale and doesn’t point to the sync guardrails described here. Removing the line numbers (or pointing at the correct module) will avoid misleading users as the code moves.
2. **kbagent sync is an ORCHESTRATOR, not cwd-per-folder.** `kbc pull` runs against
   whatever directory you `cd` into. `kbagent sync pull` *requires* `--project ALIAS`
   (resolved from a central config store) or `--all-projects` (`sync.py:67,495`). In
   CI we bridge this with env-injection: `KBAGENT_PROJECT_FROM_ENV=1` synthesizes a
   project under the reserved alias `__env__`, and every command passes

…pilot pass 2

- Replace brittle file:line citations (constants.py:425, constants.py:163,
  config_store.py:193, config_store.py:359) with stable symbol references --
  all four had drifted from the code they were pointing at.
- discover_projects now skips (with a warning) any manifest missing
  project.id or project.apiHost instead of silently emitting an invalid
  workflow (e.g. KBC_STORAGE_API_URL: https://).
- _guess_repo_slug now handles repo names containing dots (e.g. "my.repo")
  by anchoring on end-of-line and stripping an optional .git suffix, instead
  of excluding "." from the repo-name character class.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Suppressed comments (4)

plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py:149

  • The workflow generator pins uv tool install to the legacy distribution name keboola-agent-cli. In this repo, end-user installation and self-update are based on the current keboola-cli distribution name, so generated CI can end up installing the wrong package (or fail if the legacy name is not published for the requested version).
    elif version:
        spec = f"keboola-agent-cli=={version}"
    else:
        # Unpinned: only acceptable for non-production lanes. The skill warns.
        spec = "keboola-agent-cli"

plugins/kbagent/skills/kbagent-cicd-migration/references/migration-runbook.md:21

  • The version pin guidance references keboola-agent-cli==X.Y.Z, but the repo’s current distribution name used for installs/updates is keboola-cli. Aligning this avoids copy/paste installs that target the legacy package name.
- [ ] **Pick a kbagent version** and pin it (`keboola-agent-cli==X.Y.Z` or
      `git+...@vX.Y.Z`). Never unpinned on a prod lane.

plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md:10

  • The install mapping row uses keboola-agent-cli==<ver>, but this repo’s documented install/update distribution name is keboola-cli. Using the legacy name here can break reproducible installs or pin the wrong artifact.
| Download Go binary zip from `keboola/keboola-as-code` GitHub release, unzip to `/usr/local/bin/kbc` | `uv tool install keboola-agent-cli==<ver>` (PyPI) or `uv tool install 'git+https://github.com/keboola/cli@<tag>'` |

plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md:108

  • This install example uses keboola-agent-cli==<ver>, but the repo’s installation docs and current distribution name are keboola-cli. Using the legacy name here can lead to users pinning/installing the wrong package.
  `uv tool install keboola-agent-cli==<ver>` (see Step 2 for version pin) or a

Comment thread plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py Outdated
The current PyPI distribution is keboola-cli (pyproject.toml, APP_NAME_CANDIDATES
in constants.py) -- keboola-agent-cli is the legacy pre-0.63 fallback name only.
The generated CI, the runbook, and the command-mapping table were all pinning
`uv tool install` to the legacy name, which risks installing the wrong package
or failing outright once/if the legacy distribution stops being published.
…af dir

_alias_from_dir took only Path(directory).name, so nested multi-project
layouts (env/prod, other/prod) collided on the same KBC_TOKEN_<ALIAS> secret
name and generated CI would push two different projects with one token.
Sanitize the whole relative path instead.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (5)

plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md:192

  • This example command also hardcodes --version 0.58.0. To keep the runbook evergreen, use X.Y.Z (or reference “current stable version”) instead of a specific historical version.
python <skill_dir>/scripts/migrate_cicd.py /path/to/repo --write \
    --version 0.58.0 --main-branch main --schedule "0 * * * *"
**tests/test_sync_cli_behavior.py:83**
* Similarly, `sync push` has both `push` and `push_all` code paths. Adding an assertion that `push_all` wasn’t called makes this guard test fully cover the “must not reach the service” intent.
def test_push_without_project_is_usage_error(self, tmp_path: Path) -> None:
    code, mock = _invoke(["sync", "push", "--directory", str(tmp_path)], tmp_path)
    assert code == 2
    mock.push.assert_not_called()
**plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md:183**
* The migration guide hardcodes version `0.58.0` and states “PyPI, once published”, but this repo is already at 0.80.0. Using a stale example version can mislead users into pinning an unnecessarily old release (or thinking PyPI isn’t available). Prefer a placeholder (X.Y.Z) and optionally mention the current release as an example.

This issue also appears on line 190 of the same file.
  • Pinned (recommended for prod lanes): --version 0.58.0 (PyPI, once published)
    or --git-ref v0.58.0 (git tag, until PyPI exists). Reproducible CI.
**plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py:35**
* The script docstring also hardcodes `0.58.0` / `v0.58.0` and mentions “no PyPI release yet”, which is likely to go stale. Use placeholders (`X.Y.Z` / `vX.Y.Z`) so copy/paste guidance stays accurate across releases.
python migrate_cicd.py <repo_dir> [--write] \\
    [--version 0.58.0 | --git-ref vX.Y.Z] \\
    [--main-branch main] [--schedule "0 * * * *"]

Examples:
# Inspect what would change (no writes):
python migrate_cicd.py ../CLI-based-sync-demo

# Generate workflows pinned to a published PyPI version:
python migrate_cicd.py ../CLI-based-sync-demo --write --version 0.58.0

# Pin to a git tag instead (no PyPI release yet):
python migrate_cicd.py ../CLI-based-sync-demo --write --git-ref v0.58.0
**tests/test_sync_cli_behavior.py:78**
* This test asserts the service method `diff` wasn’t called, but `sync diff` also has an `--all-projects` path that calls `diff_all`. Since the docstring says usage errors must not reach the service, it’s worth asserting neither `diff` nor `diff_all` was invoked so a regression can’t slip through by accidentally defaulting to the bulk path.

This issue also appears on line 79 of the same file.
def test_diff_without_project_is_usage_error(self, tmp_path: Path) -> None:
    code, mock = _invoke(["sync", "diff", "--directory", str(tmp_path)], tmp_path)
    assert code == 2
    mock.diff.assert_not_called()
</details>

…ge-error tests

- Replace the hardcoded --version 0.58.0 / v0.58.0 examples (and the
  "PyPI, once published" / "no PyPI yet" framing) across SKILL.md and
  migrate_cicd.py with an X.Y.Z placeholder -- keboola-cli has been on PyPI
  since well before this repo reached 0.80.0, so the old examples both
  pinned a stale version and implied PyPI wasn't available yet.
- test_diff_without_project_is_usage_error / test_push_without_project_is_usage_error
  now also assert diff_all/push_all weren't called, matching the existing
  pull/pull_all guard test -- closes the gap where a regression routing a
  bad --project/--all-projects combo into the --all-projects code path
  could slip through undetected.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (5)

tests/test_sync_cli_behavior.py:142

  • This invocation also benefits from an explicit --config-dir so the CLI cannot resolve a real on-disk config via CWD traversal. (--config-dir is a global option, so it needs to be placed before the sync subcommand.)
            result = runner.invoke(
                app,
                [
                    "--json",
                    "sync",
                    "push",
                    "--project",
                    "prod",
                    "--dry-run",
                    "--directory",
                    str(tmp_path),
                ],
            )

plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py:221

  • main_branch is unused in gen_pull as well. Rename it to _main_branch (or remove it and adjust call sites) so the interface doesn’t imply the output depends on this parameter.
def gen_pull(projects: list[Project], main_branch: str, schedule: str | None) -> str:

tests/test_sync_cli_behavior.py:62

  • To keep these CLI tests hermetic and avoid accidentally picking up a developer’s real local/global config (via resolve_config_dir walking up from CWD), pass an explicit --config-dir pointing at the tmp config directory in the invocation. This reduces flakiness and makes the test intent clearer (everything should run against the temp store).

This issue also appears on line 130 of the same file.

def _invoke(args: list[str], tmp_path: Path) -> tuple[int, MagicMock]:
    """Invoke the CLI with a mocked SyncService; return (exit_code, mock)."""
    store = _store(tmp_path / "config")
    mock_sync = MagicMock()
    with (
        patch("keboola_agent_cli.cli.ConfigStore") as MockStore,
        patch("keboola_agent_cli.cli.ProjectService") as MockProj,
        patch("keboola_agent_cli.cli.SyncService") as MockSync,
    ):
        MockStore.return_value = store
        MockProj.return_value = ProjectService(config_store=store)
        MockSync.return_value = mock_sync
        result = runner.invoke(app, args)
    return result.exit_code, mock_sync

plugins/kbagent/skills/kbagent-cicd-migration/SKILL.md:40

  • The inline code reference sync.py:67,495 is brittle and already out of date as line numbers drift; it makes future verification harder because readers may not find the referenced logic. Prefer a stable file reference (optionally with the symbol/command names) instead of line numbers.
2. **kbagent sync is an ORCHESTRATOR, not cwd-per-folder.** `kbc pull` runs against
   whatever directory you `cd` into. `kbagent sync pull` *requires* `--project ALIAS`
   (resolved from a central config store) or `--all-projects` (`sync.py:67,495`). In
   CI we bridge this with env-injection: `KBAGENT_PROJECT_FROM_ENV=1` synthesizes a
   project under the reserved alias `__env__`, and every command passes

plugins/kbagent/skills/kbagent-cicd-migration/scripts/migrate_cicd.py:192

  • main_branch is unused in gen_validate, which makes the signature misleading and may trigger unused-argument lint in stricter configurations. If it’s intentionally unused, rename it to _main_branch to document that and silence unused-arg checks.

This issue also appears on line 221 of the same file.

def gen_validate(projects: list[Project], main_branch: str) -> str:

- gen_validate/gen_pull no longer take an unused main_branch parameter --
  only gen_push's environment-gating expression actually needs it.
- Fix another stale line-number reference (sync.py:67,495) in SKILL.md,
  pointing at the project-selection guard by name instead.
- Tests now pass an explicit --config-dir so the CLI invocation can't
  resolve a real on-disk config by walking up from CWD, keeping the suite
  hermetic regardless of the host environment it runs on.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Suppressed comments (1)

plugins/kbagent/skills/kbagent-cicd-migration/references/command-mapping.md:24

  • The command mapping table documents sync pull/push/diff without --project/--all-projects, but the CLI enforces that one of those selectors must be provided (see src/keboola_agent_cli/commands/sync.py, which exits with usage error when neither is set). As written, these rows will fail if copy/pasted and also contradict the skill’s own “orchestrator” guardrail narrative.
| `kbc pull -d DIR --force` | `kbagent sync pull --directory DIR --force` | `--force` overrides local-vs-remote conflicts (3-way diff) |
| `kbc push -d DIR` | `kbagent sync push --directory DIR` | Encrypts `#`-secrets fail-closed before write |
| `kbc push -d DIR --force` | `kbagent sync push --directory DIR --force` | Push's `--force` removes remote configs deleted locally (there is no `--allow-delete` flag — same flag name as pull's `--force`, but a different meaning per command) |
| `kbc push --dry-run` / push-dry action | `kbagent sync push --dry-run --directory DIR` | Shows planned changes without writing |
| `kbc diff -d DIR` | `kbagent [--json] sync diff --directory DIR` | `--json` is a **global** option (before `sync`, not after `diff`); gives structured drift for CI gating |

…diff rows

Those rows showed kbagent sync pull/push/diff with only --directory DIR,
which the CLI rejects (--project ALIAS or --all-projects is required) --
copy-pasting them verbatim would fail with a usage error and contradicted
the skill's own "orchestrator, not cwd-per-folder" guardrail.
Depends on #561 (kbagent auth pat-create/pat-revoke, v0.81.0). Updates the
migration skill to recommend minting a scoped Personal Access Token
(kbagent auth pat-create --project-id <id>) for each project's
KBC_TOKEN_<ALIAS> secret instead of pasting a raw Storage token from the
Keboola UI -- a PAT is scoped to one project, has a controllable expiry
(--ttl-days), and revokes independently of the account
(kbagent auth pat-revoke) without touching anything else that account can do.

No change to the generated GitHub Actions YAML itself: a kbc_pat_... value
is a drop-in for KBC_TOKEN under KBAGENT_PROJECT_FROM_ENV=1 (kbagent detects
the prefix and sends it as Authorization: Bearer automatically), so this is
purely a change in how the operator obtains the secret's value. The raw
Storage token path remains documented as the fallback for stacks/accounts
that can't complete auth login + TOTP step-up.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants