From 425d0f9f42b6ca10f8f953688666e849f715b90b Mon Sep 17 00:00:00 2001 From: Maxwell Date: Mon, 13 Jul 2026 18:00:23 +0200 Subject: [PATCH 1/3] docs(readme): update command status and add feature documentation --- README.md | 71 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ddbdab7..971e44c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Deno-powered CLI for managing local Docker Compose stacks across multi-service repositories, with config-driven profiles, overrides, secrets, and render pipelines. -Status: **Early development** -- all 15 feature issues are planned and tracked on the [issue tracker](https://github.com/AniTrend/stackctl/issues). +Status: **Active development** -- 14 of 15 commands implemented (see table below). --- @@ -12,28 +12,29 @@ Status: **Early development** -- all 15 feature issues are planned and tracked o | Command | Status | Description | |---|---|---| -| `stackctl init` | Planned | Generate commented `.stackctl` config | -| `stackctl generate` | Planned | Generate `stacks/*.yml` from per-service sources | -| `stackctl render` | Planned | Resolve `${VAR}` placeholders in stack files | -| `stackctl overrides` | Planned | Profile and explicit override merging | -| `stackctl up` | Planned | Deploy stacks to Docker Swarm | -| `stackctl down` | Planned | Tear down stacks | -| `stackctl status` | Planned | Show service status | -| `stackctl logs` | Planned | Follow container logs | -| `stackctl sync` | Planned | Sync images and volumes | -| `stackctl doctor` | Planned | Validate environment | -| `stackctl reload` | Planned | Re-render and reconcile without teardown | -| `stackctl secrets` | Planned | Encrypt/decrypt/deploy/clean/check with SOPS+age | -| `stackctl env` | Planned | Scaffold `.env` files from examples | -| `stackctl plan` | Planned | Dry-run summary of all operations | -| `stackctl completions` | Planned | Generate shell completions (bash/zsh/fish) | +| `stackctl init` | Implemented | Generate commented `.stackctl` config | +| `stackctl generate` | Implemented | Generate `stacks/*.yml` from per-service sources | +| `stackctl render` | Implemented | Resolve `${VAR}` placeholders in stack files | +| `stackctl up` | Implemented | Deploy stacks to Docker Swarm | +| `stackctl down` | Implemented | Tear down stacks | +| `stackctl status` | Implemented | Show service status | +| `stackctl logs` | Implemented | Follow service logs | +| `stackctl sync` | Implemented | Validate generated stacks match committed files (CI drift detection) | +| `stackctl doctor` | Implemented | Check system and project health | +| `stackctl reload` | Implemented | Re-render and reconcile without teardown | +| `stackctl secrets` | Implemented | Encrypt/decrypt/deploy/clean/check with SOPS+age | +| `stackctl env` | Implemented | Scaffold `.env` files from examples | +| `stackctl plan` | Implemented | Dry-run summary of all operations | +| `stackctl completions` | Implemented | Generate shell completions (bash/zsh/fish) | + +Override merging is integrated into `generate`, `render`, and `up` via the `--override` flag. --- ## Quick Start ```bash -# Install (once released) +# Install deno install -f --allow-run --allow-env --allow-read --allow-write \ -n stackctl jsr:@anitrend/stackctl @@ -49,6 +50,42 @@ stackctl plan --- +## Features + +- Config-driven profiles with layered overrides (`.stackctl`, `.stackctl.`, `.stackctl.local`) +- SOPS + age encrypted secrets management +- `${VAR}` render pipeline with service-local env resolution +- Docker Swarm deploy with dry-run planning +- CI drift detection via `sync` command +- Shell completions (bash/zsh/fish) + +### Secrets Subcommands + +| Subcommand | Description | +|---|---| +| `encrypt` | Encrypt `.env` files using SOPS + age | +| `decrypt` | Decrypt `.env.enc` files back to plaintext | +| `deploy` | Decrypt env files and deploy stacks | +| `clean` | Remove decrypted `.env` files securely (shred + rm) | +| `check` | Check secrets tooling availability (sops, age) | + +### Env Subcommands + +| Subcommand | Description | +|---|---| +| `list` | List `.env` files with status (present/missing/outdated) | +| `create` | Create `.env` from `.env.example` | +| `diff` | Compare `.env` against `.env.example` | +| `materialize` | Copy profile-specific env to `.env` | +| `audit` | Check for plaintext `.env` files with encrypted counterparts | + +## GitHub Actions + +A composite action for installing stackctl in GitHub Actions is available at +`.github/actions/setup-stackctl/`. See [docs/migration.md](docs/migration.md) for details. + +--- + ## Development ### Prerequisites From ea0dea7a6b454bcf2ce495d7ae28831be8928648 Mon Sep 17 00:00:00 2001 From: Maxwell Date: Mon, 13 Jul 2026 18:00:28 +0200 Subject: [PATCH 2/3] ci: add Renovate, auto-approve, autolabeler split, and homebrew-tap dispatch --- .github/release-drafter-config.yml | 12 +++++++++- .github/workflows/auto-approve.yml | 24 +++++++++++++++++++ .github/workflows/ci.yml | 6 ++--- .../workflows/release-drafter-autolabeler.yml | 21 ++++++++++++++++ .github/workflows/release-drafter.yml | 2 -- .github/workflows/release.yml | 16 ++++++++++--- renovate.json | 17 +++++++++++++ 7 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/auto-approve.yml create mode 100644 .github/workflows/release-drafter-autolabeler.yml create mode 100644 renovate.json diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml index 3ae5f25..6b76a91 100644 --- a/.github/release-drafter-config.yml +++ b/.github/release-drafter-config.yml @@ -4,30 +4,39 @@ categories: - title: '๐Ÿš€ Features' labels: - ':star2: feature' + collapse-after: 5 - title: '๐Ÿ› ๏ธ Bug Fixes' labels: - ':adhesive_bandage: bug fix' + collapse-after: 5 - title: 'โœจ Improvements' labels: - ':wrench: enhancement' + collapse-after: 5 - title: '๐Ÿ“š Documentation' labels: - ':books: docs' + collapse-after: 5 - title: '๐Ÿงฐ Maintenance' labels: - ':hammer_and_wrench: refactor' + collapse-after: 5 - title: '๐Ÿ“ฆ Dependencies' labels: - ':dagger: dependencies' + collapse-after: 5 - title: '๐Ÿงช Testing' labels: - ':test_tube: testing' + collapse-after: 5 - title: '๐Ÿšง CI/CD' labels: - ':construction_worker: ci' + collapse-after: 5 - title: 'โช Reverts' labels: - ':rewind: revert' + collapse-after: 5 autolabeler: - label: ':star2: feature' branch: @@ -47,6 +56,7 @@ autolabeler: - label: ':dagger: dependencies' branch: - '/build\/.+/' + - '/dependencies\/.+/' - label: ':test_tube: testing' branch: - '/test\/.+/' @@ -58,7 +68,7 @@ autolabeler: - '/revert\/.+/' - label: ':books: docs' files: - - '*.md' + - '**/*.md' change-template: '- $TITLE by @$AUTHOR in #$NUMBER' change-title-escapes: '\<*_&' version-resolver: diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml new file mode 100644 index 0000000..9b1a844 --- /dev/null +++ b/.github/workflows/auto-approve.yml @@ -0,0 +1,24 @@ +name: Auto Approve + +on: + pull_request_target: + types: + - opened + - synchronize + workflow_dispatch: + inputs: + pullRequestNumber: + description: Pull request number to auto-approve + required: true + +jobs: + auto-approve: + runs-on: ubuntu-latest + permissions: + pull-requests: write + if: github.actor == 'renovate[bot]' || github.event_name == 'workflow_dispatch' + steps: + - uses: hmarr/auto-approve-action@v4 + with: + review-message: "Auto approved automated PR" + pull-request-number: ${{ github.event.inputs.pullRequestNumber }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 253259c..108d492 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [main, dev] + branches: [main] pull_request: - branches: [main, dev] + branches: [main] env: DENO_VERSION: "2.x" @@ -42,7 +42,7 @@ jobs: build: runs-on: ubuntu-latest needs: check - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + if: github.ref == 'refs/heads/main' steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/release-drafter-autolabeler.yml b/.github/workflows/release-drafter-autolabeler.yml new file mode 100644 index 0000000..d80fc3c --- /dev/null +++ b/.github/workflows/release-drafter-autolabeler.yml @@ -0,0 +1,21 @@ +name: Release Drafter Autolabeler + +on: + pull_request_target: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + autolabel-pr: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter/autolabeler@v7 + with: + config-name: release-drafter-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index c2fbe86..32d7b7d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -4,8 +4,6 @@ on: push: branches: - main - pull_request: - types: [opened, reopened, synchronize] permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c8f8d6..53232b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -76,9 +76,6 @@ jobs: working-directory: dist run: shasum -a 256 stackctl-*.tar.gz > checksums.txt - # NOTE: Homebrew tap auto-update is a follow-up task. - # The release workflow currently does not trigger a Homebrew tap PR. - - name: Create Release uses: softprops/action-gh-release@v2 with: @@ -88,3 +85,16 @@ jobs: generate_release_notes: false draft: false prerelease: false + + - name: Dispatch homebrew-tap formula update + if: vars.ENABLE_HOMEBREW_DISPATCH == 'true' + continue-on-error: true + env: + GH_TOKEN: ${{ secrets.TAP_DISPATCH_TOKEN }} + run: | + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/AniTrend/homebrew-tap/dispatches \ + -f event_type='stackctl-release' \ + -f client_payload="{\"version\":\"${{ github.ref_name }}\"}" diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..a6dae11 --- /dev/null +++ b/renovate.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "addLabels": [":dagger: dependencies"], + "branchPrefix": "dependencies/", + "reviewers": ["wax911"], + "baseBranches": ["main"], + "automerge": false, + "automergeType": "pr", + "automergeStrategy": "rebase", + "packageRules": [ + { + "matchUpdateTypes": ["major"], + "automerge": false + } + ] +} From f76af6b1fcf51ad715ec5b815f0d4e056ab546ac Mon Sep 17 00:00:00 2001 From: Maxwell Date: Mon, 13 Jul 2026 18:07:10 +0200 Subject: [PATCH 3/3] ci: use GitHub App token for homebrew-tap dispatch --- .github/workflows/release.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53232b7..4462186 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,11 +86,21 @@ jobs: draft: false prerelease: false + - name: Generate App token + id: app-token + if: vars.ENABLE_HOMEBREW_DISPATCH == 'true' + uses: actions/create-github-app-token@v3 + with: + client-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: AniTrend + repositories: homebrew-tap + - name: Dispatch homebrew-tap formula update if: vars.ENABLE_HOMEBREW_DISPATCH == 'true' continue-on-error: true env: - GH_TOKEN: ${{ secrets.TAP_DISPATCH_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | gh api \ --method POST \