diff --git a/.devcontainer/post-attach.sh b/.devcontainer/post-attach.sh index 18eafb45..64978a86 100755 --- a/.devcontainer/post-attach.sh +++ b/.devcontainer/post-attach.sh @@ -4,57 +4,4 @@ set -euo pipefail repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${repo_root}" -if ! git rev-parse --git-dir >/dev/null 2>&1; then - exit 0 -fi - -template_remote_url="git@github.com:TECHLETES/python_template.git" -origin_url="$(git remote get-url origin 2>/dev/null || true)" -if [[ "${origin_url}" == "${template_remote_url}" ]]; then - echo "Current repository is the template; skipping template update check." - exit 0 -fi - -if ! git remote get-url template >/dev/null 2>&1; then - echo "Template remote not configured; adding it." - git remote add template "${template_remote_url}" - git remote set-url --push template DISABLED -fi - -current_branch="$(git symbolic-ref --quiet --short HEAD || true)" -if [[ -z "${current_branch}" ]]; then - echo "Detached HEAD; skipping template update check." - exit 0 -fi - -echo "Checking for updates from template/main..." -if ! git fetch template --prune; then - echo "Template update check failed; continuing without updates." - exit 0 -fi - -if ! git show-ref --verify --quiet refs/remotes/template/main; then - echo "Template remote exists but template/main is not available; skipping." - exit 0 -fi - -ahead_count="$(git rev-list --count HEAD..template/main)" -if [[ "${ahead_count}" -eq 0 ]]; then - echo "Template is up to date." - exit 0 -fi - -echo "Template has ${ahead_count} new commit(s)." - -if [[ -n "$(git status --porcelain=v1)" ]]; then - echo "Working tree is dirty; skipping automatic merge from template/main." - echo "Review and merge template/main manually when you are ready." - exit 0 -fi - -echo "Applying template/main without committing; review and commit or abort the merge manually." -if git merge --no-commit --allow-unrelated-histories --no-ff -m "Merge template/main into ${current_branch}" template/main; then - echo "Template updates applied without a commit. Review the staged merge, then commit or abort it manually." -else - echo "Template merge needs manual resolution." -fi +echo "Devcontainer attached. Template updates run through the Sync template workflow." diff --git a/.github/workflows/reconcile-template-sync-branch.yml b/.github/workflows/reconcile-template-sync-branch.yml new file mode 100644 index 00000000..f2fd2627 --- /dev/null +++ b/.github/workflows/reconcile-template-sync-branch.yml @@ -0,0 +1,38 @@ +name: Reconcile template sync branch + +on: + pull_request: + types: [closed] + branches: [main] + +permissions: + contents: write + +jobs: + reconcile: + if: >- + github.event.pull_request.merged == true && + github.event.pull_request.head.ref == 'chore/template-sync' && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + + steps: + - name: Check out permanent sync branch + uses: actions/checkout@v6 + with: + ref: chore/template-sync + fetch-depth: 0 + + - name: Reconcile merged template sync + shell: bash + run: | + set -euo pipefail + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git fetch origin main chore/template-sync + git switch chore/template-sync + git reset --hard origin/chore/template-sync + git merge --no-ff --no-edit origin/main + git push origin HEAD:refs/heads/chore/template-sync diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml new file mode 100644 index 00000000..5b87a9ae --- /dev/null +++ b/.github/workflows/template-sync.yml @@ -0,0 +1,22 @@ +name: Sync template + +on: + workflow_dispatch: + schedule: + - cron: "7 3 * * 1" + +jobs: + sync: + uses: TECHLETES/python_template/.github/workflows/reusable-template-sync.yml@main + + with: + template_repository: TECHLETES/python_template + template_branch: main + target_branch: main + sync_branch: chore/template-sync + config_path: .template-sync.yml + pull_request_title: "chore: sync Python template" + + secrets: + app_client_id: ${{ vars.TEMPLATE_SYNC_APP_CLIENT_ID }} + app_private_key: ${{ secrets.TEMPLATE_SYNC_APP_PRIVATE_KEY }} diff --git a/.template-sync.yml b/.template-sync.yml new file mode 100644 index 00000000..3356d017 --- /dev/null +++ b/.template-sync.yml @@ -0,0 +1,43 @@ +source: + repository: TECHLETES/python_template + branch: main + +target: + branch: main + sync_branch: chore/template-sync + +# Copied from TECHLETES/python_template/main:.template-sync.yml on 2026-07-29. +# +# The two workflows below are intentionally target-owned: this caller contains +# this repository's branch values, and reconciliation is implemented locally +# because the referenced reusable reconciliation workflow does not exist in +# the current Python template. +paths: + # Github configuration and workflows + - .github/workflows/template-sync.yml + - .github/workflows/reusable-reconcile-template-sync-branch.yml + - .github/workflows/staging-main-check.yml + - .github/workflows/ci.yml + - .github/workflows/test-devcontainer.yml + - .github/workflows/test-pre-commit.yml + - .github/workflows/test-python.yml + - .github/workflows/labeler.yml + - .github/ISSUE_TEMPLATE/** + - .github/dependabot.yml + - .github/pull_request_template.md + + # Development environment. + - .devcontainer/** + + # Python and repository tooling. + - .pre-commit-config.yaml + - .python-version + - .editorconfig + - .gitattributes + - pyproject.toml + + # Shared quality and security helpers. + - scripts/hooks/** + - scripts/adopt-template.py + + - docs/*.md