Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ jobs:
with:
dry-run: true

test-template-sync:
name: "[Test] Template Sync - Dry Run"
uses: ./.github/workflows/template-sync.yaml
permissions:
contents: write
pull-requests: write
with:
source-repo-path: devantler-tech/gitops-tenant-template
use-app-token: false
dry-run: true

ci-required-checks:
name: "CI - Required Checks"
runs-on: ubuntu-latest
Expand All @@ -160,6 +171,7 @@ jobs:
test-scan-for-todo-comments,
test-sync-cluster-policies,
test-update-agent-skills,
test-template-sync,
]
permissions: {}
if: ${{ always() }}
Expand All @@ -181,3 +193,4 @@ jobs:
${{ needs.test-scan-for-todo-comments.result }}
${{ needs.test-sync-cluster-policies.result }}
${{ needs.test-update-agent-skills.result }}
${{ needs.test-template-sync.result }}
130 changes: 130 additions & 0 deletions .github/workflows/template-sync.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: 🔄 Template Sync

on:
workflow_call:
inputs:
source-repo-path:
description: >-
The `owner/repo` path of the upstream template repository to sync from
(passed to `actions-template-sync` as `source_repo_path`). When
`use-app-token` is set, the App token is auto-scoped to read this repo
as well, so a PRIVATE template under the same owner works out of the
box; a cross-owner private template needs a custom token with read
access to it.
required: true
type: string
upstream-branch:
description: Branch of the template repository to sync from
required: false
type: string
default: main
pr-title:
description: >-
Title of the sync PR. Defaults to a Conventional-Commit `chore:` title
because every consumer squash-merges on the PR title into its changelog.
required: false
type: string
default: "chore: sync changes from the upstream template"
pr-commit-msg:
description: Commit message for the sync PR
required: false
type: string
default: "chore: sync changes from the upstream template"
pr-labels:
description: Comma-separated labels applied to the sync PR
required: false
type: string
default: dependencies,automation
pr-branch-name-prefix:
description: Prefix for the branch the sync PR is opened from
required: false
type: string
default: chore/template-sync
template-sync-ignore-file-path:
description: >-
Path to the ignore file listing consumer-owned files that must NOT be
overwritten by the template (same format as `.gitignore`).
required: false
type: string
default: .templatesyncignore
dry-run:
description: "Skip the sync and PR creation (validate the workflow interface only)"
required: false
default: false
type: boolean
use-app-token:
description: >-
When `true` (the default), open the sync PR with a GitHub App token
(minted from the `APP_ID` variable and the `APP_PRIVATE_KEY` secret)
instead of the default `GITHUB_TOKEN`. A PR opened with `GITHUB_TOKEN`
does NOT trigger the caller's `on: pull_request`/`push` CI runs, so its
required checks never report and it stays blocked; an App token avoids
this. Set to `false` to fall back to `GITHUB_TOKEN`.
required: false
default: true
type: boolean
secrets:
APP_PRIVATE_KEY:
description: >-
GitHub App private key, required when `use-app-token` is `true` (the
default). Paired with the `APP_ID` repository/organization variable to
mint an App token for opening the sync PR.
required: false

permissions: {}

jobs:
template-sync:
name: Template sync
if: ${{ !inputs.dry-run }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: 🧮 Resolve template repo name
id: template
if: ${{ inputs.use-app-token }}
shell: bash
env:
SOURCE_REPO_PATH: ${{ inputs.source-repo-path }}
run: echo "name=${SOURCE_REPO_PATH##*/}" >> "$GITHUB_OUTPUT"

- name: 🔑 Generate GitHub App token
id: app-token
if: ${{ inputs.use-app-token }}
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
Comment thread
devantler marked this conversation as resolved.
owner: ${{ github.repository_owner }}
# Scope the token to exactly the caller repo (push the sync branch +
# open the PR) AND the upstream template repo (clone it — required when
# the template is PRIVATE, harmless when it is public). Assumes both
# live under the same owner; a cross-owner private template needs its
# own read-scoped token instead.
repositories: ${{ github.event.repository.name }},${{ steps.template.outputs.name }}
permission-contents: write
permission-pull-requests: write

- name: 📑 Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# actions-template-sync needs full history to compute and merge the
# template diff. It handles its own git auth via `github_token`, so the
# checkout does not need to persist credentials.
fetch-depth: 0
persist-credentials: false
token: ${{ steps.app-token.outputs.token || github.token }}

- name: 🔄 Sync from template
uses: AndreasAugustin/actions-template-sync@8a0f668b83c32a0f673353086d74f12b8853d4f5 # v2.5.3
with:
source_repo_path: ${{ inputs.source-repo-path }}
upstream_branch: ${{ inputs.upstream-branch }}
github_token: ${{ steps.app-token.outputs.token || github.token }}
pr_title: ${{ inputs.pr-title }}
pr_commit_msg: ${{ inputs.pr-commit-msg }}
pr_labels: ${{ inputs.pr-labels }}
pr_branch_name_prefix: ${{ inputs.pr-branch-name-prefix }}
template_sync_ignore_file_path: ${{ inputs.template-sync-ignore-file-path }}
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,51 @@ jobs:

</details>

### 🔄 Template Sync

<details>
<summary>Click to expand</summary>

[.github/workflows/template-sync.yaml](.github/workflows/template-sync.yaml) keeps a repository in sync with an upstream template repository via [AndreasAugustin/actions-template-sync](https://github.com/AndreasAugustin/actions-template-sync), opening a PR with any incoming template changes. List the files this repository *owns* (and that must never be overwritten by the template) in a `.templatesyncignore` file at the repo root — everything else the template ships is kept in sync.

#### Usage

```yaml
on:
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:

jobs:
template-sync:
uses: devantler-tech/reusable-workflows/.github/workflows/template-sync.yaml@{ref} # ref
with:
source-repo-path: devantler-tech/gitops-tenant-template
secrets:
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
```

By default the sync PR is opened with a GitHub App token (`use-app-token: true`) so it triggers the caller's CI; this needs the `APP_ID` variable and the `APP_PRIVATE_KEY` secret. Set `use-app-token: false` to fall back to `GITHUB_TOKEN` (the PR then will not trigger `on: pull_request` checks).

#### Secrets and Inputs

| Key | Type | Default | Required | Description |
|----------------------------------|-----------------|--------------------------------------------------|----------|-----------------------------------------------------------------------------|
| `APP_PRIVATE_KEY` | Secret | - | When `use-app-token` | GitHub App private key (paired with the `APP_ID` variable) |
| `source-repo-path` | Input (string) | - | Yes | `owner/repo` of the upstream template to sync from |
| `upstream-branch` | Input (string) | `main` | No | Branch of the template repository to sync from |
| `pr-title` | Input (string) | `chore: sync changes from the upstream template` | No | Title of the sync PR (Conventional-Commit by default) |
| `pr-commit-msg` | Input (string) | `chore: sync changes from the upstream template` | No | Commit message for the sync PR |
| `pr-labels` | Input (string) | `dependencies,automation` | No | Comma-separated labels for the sync PR |
| `pr-branch-name-prefix` | Input (string) | `chore/template-sync` | No | Prefix for the branch the sync PR is opened from |
| `template-sync-ignore-file-path` | Input (string) | `.templatesyncignore` | No | Path to the file listing consumer-owned (non-synced) files |
| `use-app-token` | Input (boolean) | `true` | No | Open the sync PR with a GitHub App token so it triggers the caller's CI |
| `dry-run` | Input (boolean) | `false` | No | Skip the sync and PR creation (validate workflow interface only) |

> **Note:** The calling workflow runs the sync job with `contents: write` and `pull-requests: write` (declared by the reusable workflow).

</details>

### 🔄 Update Agent Skills

<details>
Expand Down
Loading