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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ Three ways in, one version number:
- **Targets as you name them** — a repository, an organisation or a group by path or by
web URL, narrowed by topics and path globs (`acme/svc-*`), minus `exclude` globs
(`corp:platform/legacy/**`)
- **Six platforms, one hub** — GitHub (github.com, GHE.com, Enterprise Server), GitLab
(gitlab.com and self-managed), Gitea, Forgejo, Bitbucket Cloud and Azure DevOps Services
(the last two built from their documentation, not yet run on a live account); one hub can
deliver to several at once, and its own CI can be any of them
- **Seven platforms, one hub** — GitHub (github.com, GHE.com, Enterprise Server), GitLab
(gitlab.com and self-managed), Gitea, Forgejo, Bitbucket Cloud, Azure DevOps Services
and Bitbucket Data Center (the last three built from their documentation, not yet run on
a live account); one hub can deliver to several at once, and its own CI can be any of
them but Bitbucket Data Center, which has none
- **Declines remembered** — content in a pull request closed without merging is not
proposed again; reopen it, or tick a box in its description, to change your mind
- **A plan before delivery** — `plan` in a hub pull request reports what every target would
Expand Down
12 changes: 7 additions & 5 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
| Program | `touchmark`, one static Go binary; the same binary in the image `ghcr.io/bedrock-python/touchmark` and the GitHub Action `bedrock-python/touchmark` |
| Requires | git 2.31+ for `check`, `status`, `apply`, `manifest`; git 2.45+ for `plan` and `distribute`, which read the targets (the image brings its own) |
| Install | archives on [GitHub releases](https://github.com/bedrock-python/touchmark/releases) · `go install github.com/bedrock-python/touchmark/cmd/touchmark@latest` (Go 1.26+) · `docker run ghcr.io/bedrock-python/touchmark:<version>` |
| Platforms | GitHub (github.com, GHE.com, GHES 3.19+), GitLab 17.0+, Gitea 1.26+, Forgejo 15+; as providers a hub delivers to: Bitbucket Cloud, Azure DevOps Services |
| Platforms | GitHub (github.com, GHE.com, GHES 3.19+), GitLab 17.0+, Gitea 1.26+, Forgejo 15+; as providers a hub delivers to: Bitbucket Cloud, Azure DevOps Services, Bitbucket Data Center 8.19+ |
| Hub template | <https://github.com/bedrock-python/engineering-assets-template> |
| Source | <https://github.com/bedrock-python/touchmark> |

Expand Down Expand Up @@ -49,7 +49,7 @@ nothing to a repository without the opt-in file, unless `targets.yml` subscribes
(`opt_in: assumed`). There is no pull mode (targets do not
fetch from the hub), no remote pack sources, and no `setup` for Bitbucket or Azure DevOps (a
hub on Bitbucket Cloud runs in Bitbucket Pipelines, one in Azure Repos in Azure Pipelines, both
set up by hand).
set up by hand; Bitbucket Data Center has no CI, so it is only ever a target).

## Mental model

Expand Down Expand Up @@ -254,7 +254,7 @@ ten `::error` and ten `::warning` annotations.
| `version` | `1` | format version |
| `id` | — | slug, 3–40 characters; names the branch `touchmark/<id>`; `change-me` fails `check` |
| `writer`, `sign` | — , `auto` | single-provider shorthand: the writer account, commit signing (`auto`, `always`) |
| `providers[]` | from CI | `id`, `type` (`github`, `gitlab`, `gitea`, `forgejo`, `bitbucket`, `azure-devops`), `url` (required for gitea, forgejo and azure-devops: `https://dev.azure.com/<organization>`), `api_url`, `ca_file`, `writer`, `known_authors`, `automation_accounts`, `sign`, `limits`; replaces the shorthand |
| `providers[]` | from CI | `id`, `type` (`github`, `gitlab`, `gitea`, `forgejo`, `bitbucket`, `azure-devops`, `bitbucket-datacenter`), `url` (required for gitea, forgejo, azure-devops: `https://dev.azure.com/<organization>`, and bitbucket-datacenter: the base URL with its context path), `api_url`, `ca_file`, `writer`, `known_authors`, `automation_accounts`, `sign`, `limits`; replaces the shorthand |
| `branch`, `branch_aliases` | `touchmark/<id>`, none | the sync branch, and former names whose pull requests stay the hub's |
| `previous_fingerprints` | none | the hub's fingerprints before it moved |
| `opt_in_file` | `.engineering-assets.yml` | the opt-in file's path in targets |
Expand Down Expand Up @@ -290,7 +290,7 @@ covers everything under it). At most 64 KiB.

`.touchmark/operations.yml` (in the hub; `touchmark schema operations`):
`recreate[]` (`target`, `head`: acts while the branch head is that commit),
`forget_declines[]` (`target`, `pr`: acts once; on Bitbucket Cloud and Azure DevOps while present), `allow_mass_close` (`max`, `until`),
`forget_declines[]` (`target`, `pr`: acts once; on Bitbucket and Azure DevOps while present), `allow_mass_close` (`max`, `until`),
`adopt_unmarked` (`until`). Dates are `YYYY-MM-DD`, UTC, inclusive.

Environment, one set per provider; `<ID>` is the provider id upper-cased with `-` as
Expand All @@ -314,7 +314,9 @@ from its environment once read:
clone (`--filter`). In CI: `fetch-depth: 0` (GitHub, Gitea), `GIT_DEPTH: "0"` (GitLab).
2. **Reader and writer are two different accounts** on every platform: two GitHub Apps,
two GitLab service accounts (or group access tokens), two Gitea or Forgejo bot users,
two Azure DevOps users with personal access tokens (Code read; Code read & write).
two Azure DevOps users with personal access tokens (Code read; Code read & write), two
Bitbucket Data Center users with HTTP access tokens (repository read; repository write;
list the reader in `automation_accounts`, since it can decline pull requests there).
The read key reaches any branch of the hub; the write key must not.
3. **The write key is visible to the default branch only.** GitHub: secrets in the
environment `touchmark-distribute` with deployment branches *Selected* = the default
Expand Down
11 changes: 11 additions & 0 deletions docs/concepts/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ that property; descriptions do carry the tick box *Rebuild this branch* while a
paused; and touchmark never reactivates an abandoned pull request, though Azure DevOps
would let it.

## On Bitbucket Data Center

Everything [above](#on-bitbucket-cloud) applies, with three differences. A person may
reopen a declined pull request there: it is then touchmark's open pull request again,
and no longer a decline; touchmark itself never reopens one. Bitbucket declines a pull
request without activity for four weeks by default, as its system user, which touchmark
counts as a bot: such a close waits for the cooldown, as a stale bot's does. And a user
who may only read a repository may decline its pull requests, the reader included: list
the reader in `automation_accounts`, so that a decline made with its token counts as a
bot's, not as the team's (`plan` warns until it is there).

## Limits

- Only the last 50 closed pull requests on the sync branches count.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ cleanup to stay safe:
| Entry | Acts | Then |
|---|---|---|
| `recreate` | while the sync branch's head is exactly `head` | a new push to the branch makes it inert |
| `forget_declines` | once: touchmark marks pull request `pr` revoked; on Bitbucket Cloud and Azure DevOps, while the entry is present (see below) | inert |
| `forget_declines` | once: touchmark marks pull request `pr` revoked; on Bitbucket Cloud, Bitbucket Data Center and Azure DevOps, while the entry is present (see below) | inert |
| `allow_mass_close` | up to `max` closes in a run, until the end of `until` (UTC) | inert after the date |
| `adopt_unmarked` | until the end of `until` (UTC) | inert after the date |

Expand Down
109 changes: 108 additions & 1 deletion docs/guide/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ A provider of type `bitbucket` is Bitbucket Cloud at `https://bitbucket.org` (le
out). `plan`, `distribute` and `doctor` work on it from a hub on GitHub or GitLab, from
a hub on Bitbucket itself, whose CI is Bitbucket Pipelines (see
[A hub on Bitbucket Cloud](../getting-started/bitbucket.md)), or wherever you run
touchmark; `setup` has nothing for it yet. Bitbucket Data Center is not supported.
touchmark; `setup` has nothing for it yet. Bitbucket Data Center has a provider type of
its own (see [Bitbucket Data Center](#bitbucket-data-center)).

```yaml
# hub.yml
Expand Down Expand Up @@ -301,6 +302,112 @@ renamed repositories answer; who can write pull request properties (whether proj
Readers, through "Contribute to pull requests", can set or change `touchmark.marker`);
and whether `Retry-After` comes on answers that went through.

## Bitbucket Data Center

A provider of type `bitbucket-datacenter` is one Bitbucket Data Center instance, 8.19 or
later, and its `url` is the instance's base URL, context path included
(`https://git.example.com/bitbucket`); the REST API is under it, at
`<url>/rest/api/latest`. `plan`, `distribute` and `doctor` work on it from a hub on GitHub
or GitLab, or wherever you run touchmark. Bitbucket Data Center has no CI of its own, so
a hub cannot live there, and `setup` has nothing for it.

```yaml
# hub.yml
providers:
- id: bbdc
type: bitbucket-datacenter
url: https://git.example.com/bitbucket
writer: touchmark-writer # the writer user's slug
automation_accounts: [touchmark-reader] # the reader: see "The read key can decline"
```

**Accounts.** Use two users that only touchmark uses, each with a personal HTTP access
token. touchmark sends every token as `Authorization: Bearer`, to the API and to git, on
every version from 8.19. Project and repository access tokens work too, but they act as a
service user Bitbucket makes for the token: it has no email address, and its name shows
only in answers (`doctor` and the report name it).

| | Reader | Writer |
|---|---|---|
| Variable | `TOUCHMARK_BBDC_READ_TOKEN` | `TOUCHMARK_BBDC_WRITE_TOKEN` |
| Token permissions | repository read | repository write; no admin |
| Repositories | read access to the targets | write access to the targets only (directly, through a group or the project); not admin, and no access to the hub |

Bitbucket's API does not tell which token a request came with: `doctor` reports the
token's expiry and permissions as unknown, so keep a reminder to rotate them.

- **Accounts are user slugs.** `writer`, `known_authors` and `automation_accounts` name
users by their slug, as their profile URL shows it (`<url>/users/<slug>`); touchmark keeps
their numeric ids.
- **Targets.** `org` is a project, by its key (`org: ACME`, or its URL
`<url>/projects/ACME`); `repo` is `<project key>/<repository slug>`, and web and clone
URLs such as `<url>/projects/ACME/repos/billing` and `<url>/scm/acme/billing.git` work.
`topics` are repository labels: an entry with `topics: [python]` takes the project's
repositories that carry the label `python`. Archived repositories are skipped, and so
are those whose default branch does not exist (as empty). A project key that lists no
repository is looked up: an unknown or changed key fails the provider's resolve, so no
stale pull request is swept on its account. Personal
repositories (`~user` projects) are not targets. A renamed repository changes its slug:
update `targets.yml`, or select it through `org`.
- **No labels.** Bitbucket Data Center has labels on repositories only, not on pull
requests: `pr.labels` sets nothing there, and touchmark records no label as set.
- **Drafts** are Bitbucket's own draft flag (`pr.draft`); on an instance that turned drafts
off, the pull request opens ready.
- **No default reviewers.** Bitbucket adds a repository's default reviewers to pull
requests made in its web interface, not to those made through the API: add reviewers
with a merge check or by hand.
- **Closing.** touchmark closes its own pull request by declining it, after writing the
close into its description. It never reopens or changes a declined pull request, though
Bitbucket would let it: a pull request touchmark closed stays closed, and the next
proposal is a new one. A person may reopen one; it is touchmark's open pull request
again.
- **Declines** are remembered as on Bitbucket Cloud, without writing to the declined pull
request: while a pull request is open, its marker records the state of the opt-in file,
and a person's decline holds while that file is parsed the same. `forget_declines` acts
while it is present (see
[Memory of declined pull requests](../concepts/memory.md#on-bitbucket-cloud)).
- **Who declined.** Bitbucket declines a pull request without activity for four weeks by
default, as its own system user: touchmark reads who declined a pull request from its
activities, and takes a decline by the system user, or by a service user, for an
automatic close, which holds the content back for `memory.auto_close_cooldown` only. Only
a person's decline holds until the opt-in file changes.
- **The read key can decline.** On Bitbucket Data Center a user who may only read a
repository may still decline its pull requests, and the reader's token is in every
branch of the hub: list the reader in `automation_accounts`, so that a decline made
with it is taken for an automatic close, not the team's decision. `plan` warns until
it is there.
- **No tick boxes, no HTML.** Bitbucket shows HTML in descriptions as text: the marker is
a Markdown link reference definition, descriptions carry no tick boxes, a paused pull
request asks for a `recreate` entry in `.touchmark/operations.yml`, and
`pr.intro_file` should hold no HTML, as on Bitbucket Cloud. A description holds 32 768
characters; touchmark keeps its own under 30 000.
- **Branch permissions** are readable with repository admin rights only, which the writer
should not have: `doctor` shows `rules` as unknown. A push a branch permission refuses
is `blocked:rules:pull-request-only` when the branch takes changes through pull
requests only, `blocked:rules:branch-permissions` for another permission, and
`blocked:rules:pre-receive-hook` for a hook (Reject Force Push, Verify Committer, the
Jira commit checker).
- **Commit author.** touchmark authors the writer's commits with the user's email address,
which links them to the user, and with `<user id>@touchmark.invalid` for a service user,
which has none.
- **Pace.** Bitbucket's rate limit gives each user a bucket of 60 requests, refilled at 5
a second, on each node, and answers 429 past it: touchmark reads 120 times a minute with
four targets at once and writes at most twice a second by default; `limits` overrides
it.

What the driver assumes from the REST reference and Atlassian's documentation, without a
live instance: that `X-AUSERNAME` names the user of every authenticated answer (how
touchmark learns who a token acts as), and the names of project and repository token
users, and whether `GET /users?filter=` finds those users (without it, such a token
cannot tell who it is, and the provider stops); that an HTML comment in a description shows as text and a link reference
definition does not; whether a declined pull request can be edited; what
`at` with `direction=OUTGOING` selects in pull request listings; whether the dashboard
lists another user's pull requests to the reader (the sweep of stale pull requests;
without it the sweep is skipped as incomplete); who the activities name for an automatic
decline; the texts of push refusals other than pull-request-only; the headers of 429
answers; and what the file APIs answer for a symlink (one may read as a file whose
content is its target) and for a missing path.

## Moving

- **A target moves to another provider on the same host** (a new App, a new group): add
Expand Down
8 changes: 4 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ of what each one would receive on every pull request to the hub.
fails the run when a branch could see the write key. See the
[security model](concepts/security.md).
- **One hub, several platforms.** GitHub (github.com, GHE.com, GitHub Enterprise Server),
GitLab (gitlab.com and self-managed), Gitea, Forgejo, Bitbucket Cloud and Azure DevOps
Services, at once from one hub. Bitbucket and Azure DevOps are built from their
documentation and not yet run on a live account.
GitLab (gitlab.com and self-managed), Gitea, Forgejo, Bitbucket Cloud, Azure DevOps
Services and Bitbucket Data Center, at once from one hub. Bitbucket and Azure DevOps are
built from their documentation and not yet run on a live account.

## Where to start

Expand Down Expand Up @@ -117,7 +117,7 @@ of what each one would receive on every pull request to the hub.

| | |
|---|---|
| Platforms | GitHub (github.com, GHE.com, GHES 3.19+), GitLab 17.0+, Gitea 1.26+, Forgejo 15+, Bitbucket Cloud, Azure DevOps Services |
| Platforms | GitHub (github.com, GHE.com, GHES 3.19+), GitLab 17.0+, Gitea 1.26+, Forgejo 15+, Bitbucket Cloud, Azure DevOps Services, Bitbucket Data Center 8.19+ |
| Runs as | a GitHub Action, a container image, a single static binary |
| Needs | git 2.31+ for `check`, `status`, `apply`; git 2.45+ for `plan` and `distribute` (the image brings its own) |
| Configuration | `hub.yml`, `targets.yml`, `.touchmark/operations.yml` in the hub; `.engineering-assets.yml` in a target |
Expand Down
Loading
Loading