Skip to content

About

A hub template for touchmark: your organisation's shared engineering files, delivered to every repository as pull requests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Engineering Assets

Your organisation's engineering standard, delivered to every repository as pull requests: agent instructions, review prompts, merge request templates and shared CI files.

This is a template. Create your own hub from it, put the files your teams share into packs, and list the repositories they belong to. Every change to a pack then arrives in those repositories as a pull request (a merge request on GitLab) that a person reviews and merges.

The work is done by touchmark (documentation). It only ever touches files it can prove it shipped: a file a team has changed stays theirs.

your-org/engineering-assets                 this hub: packs/, hub.yml, targets.yml
        │  one pull request per repository on every change to a pack
        ▼
your-org/billing, your-org/sdk-python, …    opted in, or subscribed by the hub

What's inside

packs/                                  the files you deliver, one directory per pack
  agents/                               AGENTS.md, a project profile to fill in, review and refactoring prompts
  claude/                               Claude Code instructions, skills, a reviewer subagent, settings
  python-service/                       guidelines for Python services; skills to run, test and format
  python-library/                       guidelines for Python libraries and their public API
  gitlab/                               merge request and issue templates
hub.yml                                 hub id, write account, security settings, pack descriptions and dependencies
targets.yml                             which repositories to visit and which packs they get (empty)
.touchmark/operations.yml               one-off operations, reviewed like any other change (empty)
.github/workflows/engineering-assets.yml   GitHub Actions: check and plan on pull requests, distribute and doctor
.github/dependabot.yml                  updates the pinned actions, touchmark among them
.github/CODEOWNERS                      who reviews changes to the hub, on GitHub
.gitlab-ci.yml                          the same jobs on GitLab CI
.gitlab/CODEOWNERS                      code owners on GitLab
.gitea/workflows/                       the same jobs on Gitea and Forgejo Actions
.gitea/CODEOWNERS                       code owners on Gitea and Forgejo
bitbucket-pipelines.yml                 the same jobs on Bitbucket Pipelines
azure-pipelines.yml                     the same jobs on Azure Pipelines
.azure-pipelines/touchmark.yml          the touchmark step azure-pipelines.yml runs, with the pinned image
renovate.json                           updates the pinned touchmark image (GitLab, Gitea, Forgejo, Bitbucket, Azure)
scripts/validate.sh                     runs touchmark check and actionlint before you push
LICENSE                                 MIT-0

Only packs/ is delivered; everything else configures the hub. The starter packs are yours: edit them, delete them, add your own. Nothing in the hub updates itself from upstream. Only the touchmark engine does, through a Dependabot or Renovate pull request that you review.

The CI files of all platforms ship together, and each platform reads only its own. You may delete the ones you don't use, with their CODEOWNERS.

Set up your hub

On GitHub

touchmark setup github, run from a clone of the hub with an administrator's token, does most of steps 2 to 4: it creates both Apps, the environment and the ruleset, and prints the commands that store the keys. You install the Apps. See Set up a hub's platform.

  1. Create the hub. Click Use this template and create your-org/engineering-assets. It can be private, but see Security for GitHub Free. Don't fork: forks of public repositories are always public.

  2. Create two GitHub Apps in your organisation, and install both on the repositories you will target, not on the hub:

    • reader, with Metadata, Contents and Pull requests read-only;
    • writer, with Metadata read-only, and Contents, Pull requests and Workflows read and write. touchmark asks for Workflows only for a target that needs it.
  3. Store the keys.

    • Reader: the repository variable TOUCHMARK_READ_APP_ID (the App ID) and the repository secret TOUCHMARK_READ_APP_KEY (its private key).
    • Writer: create an environment named touchmark-distribute. Set its Deployment branches and tags to Selected branches and tags, with your default branch only. Store the variable TOUCHMARK_WRITE_APP_ID and the secret TOUCHMARK_WRITE_APP_KEY in that environment, and nowhere else. Don't choose Protected branches only: with no protection rules, every branch qualifies.

    The workflow's probe job stops distribute if the write key is also visible as a repository or organisation secret, and touchmark itself checks the environment's branch policy before it writes.

  4. Protect the hub. Add a ruleset for the default branch that requires a pull request with an approving review and a review from Code Owners. Replace @acme/hub-maintainers in .github/CODEOWNERS with your team.

  5. Describe your hub. In hub.yml set id, and writer to the writer App's bot name, like acme-assets-write[bot]. List your repositories in targets.yml.

  6. Open a pull request. The check job validates the hub; the plan job reports what each repository would receive and keeps the report in a comment. Write access is checked when distribute runs, and every week by doctor.

  7. Merge it. The distribute job opens a pull request in every repository that has opted in. It runs again every day and on every merge; Run workflow runs distribute and doctor at once. The workflow starts on pushes to main and master: if your default branch has another name, add it under push: branches:.

The jobs run on ubuntu-latest. On self-hosted runners, the touchmark Action needs Linux with Docker, docker buildx and the GitHub CLI (gh), which it uses to check the image's build provenance, and HTTPS access to ghcr.io and to the Sigstore trust roots gh fetches for that check (tuf-repo-cdn.sigstore.dev and tuf-repo.github.com).

On GitLab (gitlab.com or self-managed)

touchmark setup gitlab --group <your group>, run from a clone of the hub with the token of a Maintainer of the hub who owns that group, does steps 2 to 4 through the API. See Set up a hub's platform.

  1. Create the hub. Choose New project → Import project → Repository by URL and give this repository's URL, https://github.com/bedrock-python/engineering-assets-template.git.
  2. Create two service accounts and add them to the groups that own your repositories. On instances without service accounts, use group access tokens. Keep the hub out of those groups: put it in a group or subgroup the accounts don't belong to, and add the accounts, or create the group access tokens, only on groups that hold targets and not the hub. A member of a group inherits its projects, and a group access token is a member of its group, so a writer on a group that holds the hub could push to it; touchmark setup gitlab refuses that layout, and doctor fails on it. If the hub has to stay in that group, set security.writer_on_hub: guard in hub.yml first: the writer is then a Developer of the hub, and setup and doctor make sure it can neither push nor merge to the default branch, that merge requests run the default branch's CI file and wait for it, and plan refuses a merge request the writer opened or pushed to (A writer on the hub).
    • The reader has the role Reporter and a token with read_api and read_repository. Store it as the variable TOUCHMARK_READ_TOKEN, masked but not protected: merge request pipelines need it.
    • The writer has the role Developer and a token with api and write_repository. Store it as TOUCHMARK_WRITE_TOKEN: Protected, Masked and hidden, environment scope touchmark-distribute. Don't add the writer to the hub project.
  3. Lock down the hub.
    • Protect only the default branch (Allowed to push: No one) and don't create protected tags.
    • Set Minimum role to use pipeline variables to No one allowed, and keep Allow merge request pipelines to access protected variables off.
    • The probe job fails a merge request pipeline that can see the write token. Run touchmark doctor --hub-token once with a Maintainer token to check the variable's settings.
    • Replace @acme/hub-maintainers in .gitlab/CODEOWNERS with your group. Requiring code owner approval needs Premium.
    • Every job runs in the touchmark image, so the hub's runners must honor image:: GitLab.com's hosted runners do, and so do the Docker, Docker Autoscaler and Kubernetes executors. The Shell executor ignores image:, and the jobs fail.
    • If your runners can't pull from ghcr.io, change the image in .gitlab-ci.yml in a reviewed merge request. The image is pinned by digest on purpose: an overridable variable could leak the token.
  4. Schedule the runs. Under Build → Pipeline schedules, create two schedules for the default branch: a daily one described touchmark distribute and a weekly one described touchmark doctor. The doctor job runs in schedules whose description contains "doctor"; every other schedule runs distribute.
  5. Describe your hub. In hub.yml set id, and writer to the writer's username. For a self-managed instance, describe it under providers instead, with its url and writer, so that touchmark also knows it outside CI. List your repositories in targets.yml.
  6. In your target projects, turn on the CI/CD job token allowlist (Limit access to this project). Sync pipelines run as the writer, so their job token carries the writer's access to your other projects. Use a separate writer for groups you trust differently.
  7. Open a merge request and check the check, plan and probe jobs. The plan report is attached to the merge request.
  8. Merge it. The distribute job opens a merge request in every project that has opted in. Run pipeline on the default branch runs distribute and doctor.

On Gitea or Forgejo

Gitea and Forgejo Actions can't limit a secret to the default branch, so touchmark won't use their CI with security.write_isolation: platform. Pick one:

  • run the hub's CI on GitHub or GitLab, and deliver to Gitea or Forgejo from there: add them as providers in hub.yml and their keys next to the others;
  • host the hub on Gitea or Forgejo, set security.write_isolation: none with a reason, and protect every branch (*) so that only maintainers can push. Contributors then work from forks, which receive no secrets.

For a hub on Gitea or Forgejo:

  1. Create the hub by migrating this repository.
  2. Create two bot users: a reader in a team with read access and a writer in a team with write access to your repositories. Give the teams your target repositories one by one, not all repositories of the organisation, which would include the hub. Reader token: read:repository, read:issue, read:organization, read:user. Writer token: write:repository, write:issue, read:organization, read:user. Never use an admin token. Store them as the Actions secrets TOUCHMARK_READ_TOKEN and TOUCHMARK_WRITE_TOKEN.
  3. Check the runner label. The workflows in .gitea/workflows/ run on ubuntu-latest, the label of Gitea's default runners. If your runners are registered with other labels, change runs-on. Every job runs in the touchmark image and clones the hub with git, so it needs no actions from github.com or a mirror. The report is in the job's log, and on Gitea 1.27 with runner 2.0 in its summary.
  4. Protect the hub as above, and replace @acme/hub-maintainers in .gitea/CODEOWNERS with your team.
  5. Describe your hub. Set id and writer in hub.yml, then open a pull request and merge it. distribute runs on every merge to main or master (add your default branch under push: branches: if it has another name) and every day; doctor every Monday, from .gitea/workflows/engineering-assets-doctor.yml.

On Bitbucket Cloud

Not verified on a live Bitbucket yet. The Bitbucket support follows Atlassian's documentation and is tested against a stand-in of its API. The points a first live run must confirm are in A hub on Bitbucket Cloud.

Bitbucket Pipelines gives a deployment environment's variables to every step that names the environment, in any branch's bitbucket-pipelines.yml. Only the environment's deployment permissions, a Premium feature, stop the steps of other branches, and Bitbucket's API does not show them, so touchmark cannot check them. Pick one:

  • Premium: keep security.write_isolation: platform, restrict the environment touchmark-distribute to the default branch and to admins or selected users, and say so in security.reason. Without that reason, touchmark refuses to run distribute on Bitbucket.
  • Free or Standard: set security.write_isolation: none with a reason. Every branch of the hub can then read the write key: let only maintainers push, and keep write access to the hub tight. Or run the hub's CI on GitHub or GitLab, and deliver to Bitbucket Cloud from there.

touchmark setup has nothing for Bitbucket yet, so these steps are by hand:

  1. Create the hub. In your workspace choose Create → Repository → Import repository, give this repository's URL, https://github.com/bedrock-python/engineering-assets-template.git, and turn Pipelines on (Repository settings → Pipelines → Settings).

  2. Create two bot accounts with an API token each: a reader with read access to your target repositories and the scopes read:user, read:workspace, read:repository and read:pullrequest, and a writer with write access to the targets only (no admin, no access to the hub) and those scopes plus write:repository and write:pullrequest (all …:bitbucket).

  3. Create the hub's access token (Repository settings → Security → Access tokens) with Repositories: Read and Pull requests: Write. Pipelines gives a step no API token: touchmark reads the default branch with this one, and keeps plan's comment with it.

  4. Store the keys, all Secured:

    • repository variables TOUCHMARK_READ_TOKEN (the reader's token) and TOUCHMARK_PIPELINES_TOKEN (the hub's access token);
    • create the deployment environment touchmark-distribute (Repository settings → Deployments) and store the writer's token as its variable TOUCHMARK_WRITE_TOKEN, and nowhere else. On Premium, allow deployments to it from the default branch only, and only by admins (Only allow admins to deploy) or selected users or groups: the branch alone is not enough, since anyone with write access to the hub can run the custom pipelines on the default branch with variables of their choosing.

    The probe step runs first in every pipeline, without the deployment, and stops the pipeline if it can see the write token: then every branch could.

  5. Protect the hub. Under Branch restrictions, let no one push to the default branch directly and only maintainers merge into it; keep the bot accounts and the access token out. Bitbucket reads none of the CODEOWNERS files: replace @acme/hub-maintainers in them, or delete them with the other platforms' CI files.

  6. Schedule the runs. Under Pipelines → Schedules, on the default branch: a daily schedule for the custom pipeline distribute, and a weekly one for doctor.

  7. Describe your hub. In hub.yml set id, writer to the writer account's UUID in quotes, like "{3f2a8d4e-…}", and security as above. In targets.yml use org: <workspace> with match, or repo: <workspace>/<repository>: Bitbucket has no topics.

  8. Open a pull request and check the probe, check and plan steps. plan keeps its report in a comment; the report files are the step's artifacts.

  9. Merge it. The push pipeline runs probe, then distribute, which opens a pull request in every repository that has opted in. Run pipeline runs the custom pipelines distribute and doctor. The push pipeline starts on main and master: if your default branch has another name, add it to the branches pattern.

On Azure DevOps

Not verified on a live Azure DevOps yet. The Azure Pipelines support follows Microsoft's documentation and is tested against a stand-in of its API. The points a first live run must confirm are in A hub on Azure DevOps.

The hub lives in Azure Repos of Azure DevOps Services, and azure-pipelines.yml runs touchmark with docker on Microsoft-hosted Ubuntu agents. Any branch's copy of that file could link the variable group that holds the write key: the group's Branch control check, admitting the default branch only, is what keeps every other branch out. touchmark cannot read that check from the job, so under security.write_isolation: platform it refuses to run distribute until security.reason states the check; touchmark doctor --hub-token then verifies it with a maintainer's token.

touchmark setup has nothing for Azure DevOps yet, so these steps are by hand:

  1. Create the hub. In your project choose Repos → Import repository and give this repository's URL, https://github.com/bedrock-python/engineering-assets-template.git. Then Pipelines → New pipeline → Azure Repos Git, pick the hub and Existing Azure Pipelines YAML file, /azure-pipelines.yml, and save it without running.

  2. Create two bot users in the organization (Basic access), each with a personal access token for this organization only: a reader with read access to your target repositories and the scope Code (read), and a writer with Contribute, Create branch and Contribute to pull requests on the targets only (no administration, no access to the hub) and Code (read & write). Tokens expire: note when.

  3. Store the keys, all secret:

    • the pipeline's variable TOUCHMARK_READ_TOKEN (the reader's token; Edit → Variables, Keep this value secret, and not Let users override this value);
    • the variable group touchmark-distribute (Pipelines → Library) with the writer's token as TOUCHMARK_WRITE_TOKEN, and nowhere else. Under its Pipeline permissions allow the hub's pipeline alone, and under Approvals and checks add Branch control with Allowed branches refs/heads/main (your default branch), Verify branch protection on and Allow unknown status off.

    The probe stage runs first in every run, without the group, and stops the run if it can see the write token: then every branch could.

  4. Create the environment touchmark-distribute (Pipelines → Environments, no resources): distribute and doctor run as deployment jobs to it, and touchmark refuses them anywhere else. Add the checks Exclusive lock, so that two runs never overlap, and the same Branch control.

  5. Protect the hub. Under Project settings → Repositories → the hub → Policies, on the default branch: add Build validation with the hub's pipeline, required (that is what runs the pull request builds: Azure Repos ignores pr: triggers); add your maintainers as automatically included reviewers, required; require a minimum number of reviewers with Reset all approval votes on new changes, and Prohibit the most recent pusher from approving. A pull request build runs its own branch's YAML with the job access token, so a review policy that any vote satisfies is not enough. Azure DevOps reads none of the CODEOWNERS files: delete them with the other platforms' CI files. Under Organization settings → Pipelines → Settings turn on Limit variables that can be set at queue time (otherwise whoever may queue a run of the default branch sets variables in the steps that hold the write key), Limit job authorization scope to current project and Protect access to repositories in YAML pipelines.

  6. Optionally, let the pipeline comment. plan keeps its report in a pull request comment with the job's own token only if <project> Build Service (<organization>) may Contribute to pull requests on the hub (Project settings → Repositories → the hub → Security). That right also lets a pull request build vote on pull requests, so grant it only with your maintainers as required reviewers (step 5). Without it the report is still in the run's summary and artifacts.

  7. Describe your hub. In hub.yml set id, writer to the writer user's identity id (GET https://dev.azure.com/<organization>/_apis/connectionData with its token shows it as authenticatedUser.id), and in security keep write_isolation: platform with a reason that states the Branch control check of step 3. In targets.yml use org: <organization> with match: ["<project>/*"], or repo: <project>/<repository>: Azure DevOps has no topics.

  8. Open a pull request and check the probe, check and plan stages. The first run asks you to permit the pipeline to use the variable group and the environment. The report files are the run's artifacts, its summary shows them, and with step 6 plan also keeps the report in a closed comment thread. touchmark doctor --hub-token --hub-fp dev.azure.com/<repository id>, from a clone of the hub with a maintainer's token, checks steps 3 to 5.

  9. Merge it. The run of the default branch does probe, then distribute, which opens a pull request in every repository that has opted in. The schedules run distribute every day and doctor every Monday; Run pipeline asks which of the two to run. The triggers and conditions name main and master: if your default branch has another name, add it there.

Opt a repository in

Nothing happens to a repository until it has .engineering-assets.yml at its root, unless the hub subscribes it (below). The file can be empty: its presence is the consent. It can also ask for more packs and keep some paths for itself, or say enabled: false to opt out:

version: 1
packs: [claude]                 # in addition to what the hub assigns
ignore:
  - .claude/settings.json       # we keep our own
  - .agents/guidelines/**

Editing packs or ignore in this file is also how a team changes its mind: content it declined by closing a sync pull request is proposed again after the change. Comments and formatting don't count.

The hub decides the minimum every target gets in targets.yml. A repository can add packs and ignore paths, but it can't remove a pack the hub assigns.

# targets.yml
version: 1
defaults:
  packs: [agents]
targets:
  - repo: your-org/billing
    packs: [python-service]
  - org: your-org               # on GitLab: group: your-org/platform
    topics: [python-library]
    packs: [python-library]
  - repo: corp:platform/api     # a repository on another provider from hub.yml
  - repo: https://gitlab.example.com/platform/web   # or its web URL
  - org: your-org
    match: [your-org/svc-*]     # only the repositories whose path matches
    opt_in: assumed             # subscribed by the hub: no opt-in file needed
exclude:
  - your-org/*-archive          # * within a segment, ** across segments, ? one character
  - corp:platform/legacy/**

A web URL names a repository or a namespace of the provider in hub.yml whose url it lies under; reports name every target <provider>:<path>.

When the hub subscribes. An entry with opt_in: assumed (or defaults.opt_in: assumed) makes the repositories it selects count as opted in without the file: they get the packs of targets.yml, and their first sync pull request says the hub subscribed them. Closing it is a decline, remembered as for any target. A team chooses packs or ignores files by adding .engineering-assets.yml, and opts out with enabled: false in it; deleting the file of a subscribed repository returns it to the hub's subscription.

The starter packs

Pack What a repository gets Needs
agents AGENTS.md, the instructions every coding agent reads; .agents/project.md, a project profile for the team to fill in; the prompts .agents/prompts/review.md (a review), refactor-check.md (before a refactoring) and pure-refactor.md (a check that a change keeps behaviour) —
claude CLAUDE.md, which imports AGENTS.md and the profile; .claude/settings.json, which only denies reading secrets; the skills /review-change, /spec, /new-branch, /commit and /pr; the reviewer subagent agents
python-service .agents/guidelines/python.md, testing.md and database.md; the skills /app-start, /app-stop, /test and /fmt; the test-runner subagent agents
python-library .agents/guidelines/python.md, testing.md and public-api.md agents
gitlab .gitlab/merge_request_templates/Default.md, .gitlab/issue_templates/Bug.md and Feature.md —

How they fit together:

  • Shared instructions, local facts. The shared files never name a tracker, a branch pattern or a build command. They tell agents to read .agents/project.md, where each team writes those facts. The profile ships as a skeleton: once a team edits it, it belongs to the repository. Until then, AGENTS.md has agents find the facts in the repository (build files, CI, CONTRIBUTING.md, git), say where they found them and offer to fill the profile, so no repository depends on an empty file.
  • One source for every agent. Codex, Cursor, Copilot and Claude Code read AGENTS.md; CLAUDE.md imports it for Claude Code. The review checklist lives once in .agents/prompts/review.md, and the Claude skill and subagent follow it. The prompt splits a large change into slices, reviewed in parallel where the agent can run sub-agents and one after another where it cannot, then looks at the change as a whole.
  • Skills with side effects wait to be asked. /new-branch, /commit, /pr, /spec, /app-start and /app-stop run only when you invoke them. Skill names avoid Claude Code's built-in commands, such as /review and /branch.
  • No hooks, no extra permissions. .claude/settings.json holds deny rules only. Read(!.env.example) keeps .env.example readable: in a deny list, a rule that starts with ! carves its paths out of the rules listed before it in the same file (Claude Code's permission rules).
  • Guidelines stay out of docs/. Many projects publish docs/ as their documentation site, and the guidelines are for the people and agents who change the code, not for its users: they live in .agents/guidelines/, next to the profile and the prompts.
  • python-service and python-library both ship .agents/guidelines/python.md and testing.md, with different content: give a repository one of them.

GitHub reads pull request and issue templates, CODE_OF_CONDUCT.md and SECURITY.md from your organisation's .github repository when a repository has none, so there is no GitHub pack. Add one if those files must live in every repository.

Write packs

  • Layout. A pack is a directory in packs/, laid out like the target repository: packs/agents/AGENTS.md becomes AGENTS.md. Every file in it is delivered, so describe a pack in hub.yml, not in a README inside it.
  • Order. Packs apply in order; if two ship the same path, the later one wins. Declare dependencies under packs: in hub.yml with requires, and touchmark orders the packs for you.
  • Renames. When you rename a pack, list its old name under formerly in hub.yml, so files the old name shipped stay managed.
  • Size. Every file must be at least 64 bytes: smaller content can't prove where it came from.
  • Reserved file. Don't ship .engineering-assets.yml. It belongs to each repository.
  • Seed files. A file that a team is expected to fill in, like .agents/project.md, can ship as a skeleton. Once edited, it belongs to the repository and is never touched again.
  • Project facts. Keep tracker keys, branch patterns, the pull request language and build commands out of shared files. They belong in the project profile, which the shared instructions tell agents to read.
  • Commit first. touchmark ships what is committed on the default branch, not your working tree.

Day to day

To… Do this
change a shared file everywhere edit it in the pack, open a pull request in the hub, check plan, merge
let one repository keep its own version edit the file in that repository (it becomes local), or add it to ignore
bring a diverged file back under the hub run touchmark apply --adopt <path> in that repository (add --assume-opt-in when the hub subscribes it through an org: or group: entry and it has no opt-in file) and open a pull request there
retire a file delete it from the pack; repositories that never changed it get a pull request deleting it
add a repository add it to targets.yml; the next run visits it once it has opted in, or at once with opt_in: assumed
undo a closed sync pull request reopen it, or tick Propose this content again in its description
resume a sync branch someone pushed to tick Rebuild this branch in the pull request, or add a recreate entry with the branch head to .touchmark/operations.yml
move a target to another provider on the same host add the old write account to known_authors in hub.yml
check the write account read the weekly doctor job, or run the workflow by hand
upgrade touchmark read the release notes, then merge the Dependabot or Renovate pull request. Dependabot's plan gets no secrets and shows the hub's side only; Renovate opens its merge request once you approve it on the Dependency Dashboard, and its plan checks the targets

Validate locally

scripts/validate.sh runs what the CI runs, before you push:

bash scripts/validate.sh             # touchmark check, actionlint on .github and .gitea workflows, the Bitbucket image pin
bash scripts/validate.sh --release   # also fail unless touchmark is pinned to a published release

It checks a fresh copy of your working tree, committed or not, as a hub created from it would look. It needs touchmark on your PATH, or the command that runs it in TOUCHMARK, and actionlint or Docker. The GitLab CI file is checked by GitLab itself: use CI Lint in the project; bitbucket-pipelines.yml by Bitbucket's editor. --release fails while bitbucket-pipelines.yml still waits for that release.

Security

A hub can open pull requests in every repository it targets, so treat it as the root of your supply chain:

  • A merge into the hub runs code in your targets' CI: a sync pull request runs the target's workflows with its secrets before anyone reviews it. Protect the default branch, require review, and keep CODEOWNERS on packs/, hub.yml, targets.yml, .touchmark/ and the CI files.
  • Keep the write account on the default branch only: the touchmark-distribute environment on GitHub, a protected, environment-scoped variable on GitLab, a deployment variable of touchmark-distribute restricted to the default branch and to admins or selected users on Bitbucket Premium. The probe checks that no other job can see the key, and distribute refuses to run otherwise, unless hub.yml sets security.write_isolation to external, or to none with a reason. A private hub on GitHub Free can't pass: make the hub public, use the Team plan, or release the key from an external secret store through OIDC.
  • The hub's maintainers hold the write account on every platform: they can change the variables, the environments and the workflows. Choose them accordingly.
  • One-off operations that override a safeguard live in .touchmark/operations.yml and go through review. The workflows take no inputs, and touchmark refuses those flags in CI.
  • The read account can read every target, and anyone who can push a branch to the hub can use it. Keep hub write access tight.
  • touchmark is pinned, and upgraded through review: the Action by commit, and the image by digest on GitLab, Gitea, Forgejo and Bitbucket. The Action runs the image of its own release, by digest, and only after gh attestation verify has shown that touchmark's publish workflow built it. Dependabot's pull requests get no secrets, so their plan checks no target: it shows the hub's side, warns, and passes. Read the release notes, and run plan with the read key yourself before you merge. Renovate waits a week for other updates, but GHCR gives no release dates, so for the touchmark image it waits for your approval on its Dependency Dashboard instead.
  • Read the ⚠ section of every sync pull request. It lists changes to workflows, CI configuration, agent settings, skills and subagents, and CODEOWNERS.
  • In a public repository, GitHub disables scheduled workflows after 60 days without activity. Push to the hub or re-enable the workflow if the daily run stops. On GitLab, a schedule stops when its owner loses access: give it to an account that stays.
  • On Bitbucket, anyone with write access to the hub can run its custom pipelines and pass them variables, which override deployment variables. The template's scripts reference none, so the website asks for none, but the API takes any. touchmark's git ignores the variables that would change its config or the programs it runs (GIT_CONFIG_*, GIT_SSH_COMMAND, LD_PRELOAD, …), yet the rest of the run's environment is the caller's: on Premium, restrict touchmark-distribute to admins or selected users, so that deployment permissions pause anyone else's run.
  • A public hub skips private targets and prints only how many, because anyone can read its CI logs.

touchmark never merges. It never overwrites or deletes a file a repository has changed.

License

The template and its starter packs are released under MIT-0: use and change them without attribution. touchmark itself is Apache-2.0.

About

A hub template for touchmark: your organisation's shared engineering files, delivered to every repository as pull requests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages