Skip to content

Fix release CHANGELOG push blocked by branch rules - #307

Merged
petesramek merged 110 commits into
preview/1.0from
develop/1.0
Oct 29, 2026
Merged

petesramek merged 110 commits into
preview/1.0from
develop/1.0

Conversation

@petesramek

Copy link
Copy Markdown
Owner

The Update CHANGELOG.md job failed with GH013 (changes must go through a PR) on release/1.0. Both the CHANGELOG and PublicAPI.Unshipped pushes now use artifact + branch-protection unlock + git/push-changes + lock, matching promote-api-files. Remaining pushes (tags, new branches) don't target protected existing branches.

petesramek and others added 30 commits April 4, 2026 10:23
…inks, add released versions table (#174)

Previously, versioned folders contained only API reference YAMLs while
guide pages were global (unversioned). The root `index.md` had entirely
broken links. The version switcher navigated to a bare `/{ver}/` folder
that has no `index.html`, causing 404s on GitHub Pages.

## Workflow (`publish-documentation.yml`)

- **Guide snapshot**: copies `api-reference/guide/` into
`api-reference/{ver}/guide/` immediately after API metadata generation,
so each version folder contains a full snapshot of both guide and API
reference
- **DocFX version groups**: each group now registers two content entries
— API YAMLs (`*.yml`) and guide pages (`guide/*.{md,yml}`) — producing
`_docs/{ver}/` and `_docs/{ver}/guide/` in the build output
- **Global TOC**: `Guide` nav link now points to
`{latest}/guide/introduction.html` instead of the unversioned `guide/`
- **index.md injection**: a single step builds the released versions
table and substitutes both `{version}` and `{versions_section}`
placeholders before `docfx build` runs (no committed artefacts)

## `api-reference/api-reference.json`

Removed `guide/*.{md,yml}` from the global content entry — guide pages
now exist only within versioned groups.

## `api-reference/index.md`

Replaced broken links with `{version}`-placeholder paths resolved at
build time. Added a `{versions_section}` placeholder that the workflow
expands into a full released-versions table, e.g.:

| Version | Guide | API Reference |
|---------|-------|---------------|
| v2.0 (latest) | [Guide](2.0/guide/introduction.html) | [API
Reference](2.0/PolylineAlgorithm.html) |
| v1.0 | [Guide](1.0/guide/introduction.html) | [API
Reference](1.0/PolylineAlgorithm.html) |

## `docs-versioning/public/version-switcher.js`

Fixed the fallback navigation branch (triggered when the user is not
inside a versioned path) to resolve the site root via the `docfx:rel`
meta tag and navigate to `/{ver}/PolylineAlgorithm.html` instead of the
bare `/{ver}/` folder.
…ced with plain nav links (#177)

The version picker was buried on the right side of the navbar next to
search and theme controls, and the Reference nav entry used a
per-version dropdown while Guide was a plain link — inconsistent and
counterintuitive.

## Changes

### Navbar layout (`_master.tmpl`)
- Removed `ms-auto` from `#version-picker-container` — it was already
before `#navbar` in DOM order, so dropping the margin pushes it left
- Added `flex-grow-1` to `#navbar` so search/theme controls remain
right-aligned

Result: `[Logo] [v1.1 ▾] [Guide] [API Reference] ——— [Search] [Theme]`

### CI toc.yml generation (`publish-documentation.yml`)
- Replaced the "Reference dropdown" step (loop over all versions →
`dropdown: true` + `items:`) with two plain links — `Guide` and `API
Reference` — both pointing to the latest version
- The version picker is now the single source of truth for version
selection

### Nav link version sync (`version-switcher.js`)
- Added `updateNavLinks(version)` — rewrites `href` on all same-origin
navbar `<a>` elements to reflect the active version
  - Uses `new URL(link.href)` for normalised absolute URL handling
  - Same-origin guard eliminates any `javascript:` scheme path
- Version input validated against `/^\d+(\.\d+)*$/` before any DOM
mutation
  - Extracted `VERSION_PATTERN` constant shared across the module
- Called on `window.load` (syncs links to the version in the current
URL) and on version-change before navigation

### Static toc.yml placeholder
- Added `API Reference` entry (pointing to `1.0/`) to reflect the
intended nav structure
…#181)

Version switcher on the global index page produced broken URLs like
`.../index.html/1.0/PolylineAlgorithm.html`, and even when corrected
would land users on the raw API reference — a poor first-visit
experience. Introduction was also redundant as a separate guide entry.

## Changes

### `version-switcher.js`
- **URL fix**: `getSiteRoot()` treated an empty `docfx:rel` meta
attribute as a valid relative URL, causing `new URL('',
'.../index.html')` to resolve to the full page path including the
filename. Now falls back to `'./'` when the attribute is empty.
- **Better redirect from index**: Selecting a version on the global
index now navigates to `{version}/guide/getting-started.html` instead of
`{version}/PolylineAlgorithm.html`.

```js
// Before
return meta ? meta.getAttribute('content') : './';
// After — empty string is falsy, so we fall back to './'
return meta ? (meta.getAttribute('content') || './') : './';
```

### `api-reference/guide/toc.yml`
- Removed the **Introduction** entry; **Getting Started** is now the
first and default page in the versioned guide sidebar.

### `api-reference/index.md`
- Replaced the bare link list with a proper landing page: library
summary, key benefits, and a quick-start install snippet — content drawn
from `introduction.md`. The `{versions_section}` placeholder is
preserved for workflow injection.

### `publish-documentation.yml`
- Navbar `toc.yml` generation and the versions table both updated to
link to `getting-started.html` instead of the now-removed
`introduction.html`.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
…itions (#184)

## Summary

Adds `.github/workflows/backtrack.yml` — an automated workflow that
back-merges changes from higher branches into lower branches after a PR
is merged, with branch locking to prevent race conditions.

## Background

Currently the back-merges (e.g. `Merge branch 'preview/1.0' into
develop/1.0`) are done manually. Without any coordination, a developer
could push a commit to `develop/M.n` between the moment the workflow
reads the branch tip and when it pushes the merge — causing the push to
fail or producing an incorrect merge base.

## What the workflow does

**Trigger**: any PR that is _merged_ into `preview/**` or `release/**`

| Source (base_ref) | Backtrack cascade |
|---|---|
| `preview/M.n` | `preview/M.n → develop/M.n` |
| `release/M.n` | `release/M.n → preview/M.n → develop/M.n` |

**Race-condition protection**:
1. `develop/M.n` is **locked** (branch protection applied) before any
merge begins — blocking concurrent direct pushes from developers.
2. All pushes use `GH_ADMIN_TOKEN`, which bypasses `enforce_admins:
false` protection — so the workflow can push even to a locked branch.
3. `develop/M.n` is **unlocked** in an `always()` step keyed on
`steps.lock-develop.outcome != 'skipped'` — ensures cleanup even if a
merge fails.
4. `preview/M.n` is already permanently locked by `promote-branch.yml`
and does not need extra lock/unlock steps; the admin token bypasses it
for the release→preview merge.

## Impact on other workflows

- **`bump-version.yml`**: unaffected — it creates brand-new branches via
`workflow_dispatch` (no PR merge event) and pushes to branches that have
no protection yet.
- **`promote-branch.yml`**: unaffected — it handles forward promotion
and creates/locks branches on first creation.
- **`release.yml`**: unaffected — it handles building, publishing, and
creating support branches.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
…ed (#186)

- [x] Understand the gap: base branch (`github.base_ref`) was not locked
during backtracking, allowing new PR merges into it concurrently
- [x] Lock base branch (`github.base_ref`) with `lock-branch: 'true'`
before the merge steps
- [x] Restore permanent protection on base branch after backtrack
(always, guarded by lock step outcome)
- [x] Initial parameterization of lock/unlock actions
- [x] Lock action: replaced `bypass-users` (user list) with
`bypass-admins` boolean — `true` sets `enforce_admins: false` so admins
are exempt from all rules; default is `false` (admins are also subject
to rules)
…re active (#191)

The backtrack workflow's automated `POST /repos/.../merges` calls were
blocked by branch protection: `enforce_admins: true` combined with
`required_pull_request_reviews` rejects the Merges API even for admin
tokens — there's no bypass.

## Changes

**`lock/action.yml`** — adds `skip-pull-request-reviews` input. When
`true`, sends `required_pull_request_reviews: null` in the PUT payload
instead of the review config object:

```yaml
"required_pull_request_reviews": (if $skip_reviews then null else {
  "dismiss_stale_reviews": $dismiss_stale,
  "require_code_owner_reviews": false,
  "required_approving_review_count": $review_count
} end)
```

**`backtrack.yml`** — the two "unlock for merge" steps (preview and
develop branches) now pass `skip-pull-request-reviews: 'true'`. The
existing `always()`-guarded restore steps re-apply full PR review
requirements after each merge regardless of outcome.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
…omated merges (#193)

The backtrack workflow locked concrete branches (e.g., `preview/1.0`,
`develop/1.0`) during automated merges but never properly cleaned them
up — the "restore" steps called the `lock` action with `lock-branch:
false`, which issues a `PUT` that modifies the rule but leaves the full
protection ruleset in place. Branches with no prior protection ended up
permanently protected after each run.

## Changes

- **`backtrack.yml`** — Replace all three "Restore protection" steps
with the `unlock` action (`DELETE /branches/{branch}/protection`)
instead of `lock` with `lock-branch: false` (`PUT
/branches/{branch}/protection`). Concrete per-branch rules are fully
removed after the merge; the manually-maintained glob pattern rules
(`develop/**`, `preview/**`, `release/**`) remain as the base
protection.

```yaml
# Before — PUT: flips lock_branch off but leaves the full ruleset behind
- uses: './.github/actions/github/branch-protection/lock'
  with:
    branch: ${{ steps.targets.outputs.develop-branch }}
    token: ${{ secrets.GH_ADMIN_TOKEN }}
    lock-branch: 'false'

# After — DELETE: removes the temporary concrete rule entirely
- uses: './.github/actions/github/branch-protection/unlock'
  with:
    branch: ${{ steps.targets.outputs.develop-branch }}
    token: ${{ secrets.GH_ADMIN_TOKEN }}
    fail-on-error: 'true'
```
…ired_linear_history in branch protection payload (#196)

The backtrack workflow was failing with HTTP 409 ("This branch must not
contain merge commits") because the `preview/*` branches have
`required_linear_history: true` in their protection. The lock action's
payload omitted the field entirely, so GitHub preserved the existing
`true` value even during the "unlock for merge" phase.

## Changes

- **`lock/action.yml`** — Added `required-linear-history` input
(default: `false`) and included `"required_linear_history":
$required_linear_history` in the branch protection PUT payload. Because
the default is `false`, the existing "unlock for merge" calls in
`backtrack.yml` now explicitly send `false` without requiring any
call-site changes, overriding the previously preserved `true` value
before the merge API is invoked.

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
…omposite action (#203)

- [x] Add `develop/**` to `branches` filter in
`.github/workflows/pull-request.yml`
petesramek and others added 27 commits October 29, 2026 11:44
#277)

The "Promote PublicAPI files (Unshipped -> Shipped)" job fails when it
re-locks `release/1.0`. The `PUT .../protection` call returns HTTP 422
because the restored settings contain `dismissal_restrictions: null` and
`bypass_pull_request_allowances: null`. The API requires these keys to
be objects or omitted.

- **`branch-protection/lock/action.yml`**
- When `protection-settings` is supplied, null-valued keys inside
`required_pull_request_reviews` are dropped before the PUT.
- Top-level nulls such as `restrictions: null` and
`required_status_checks: null` are untouched, since the API accepts
them.

```bash
payload=$(echo "$PROTECTION_SETTINGS" | jq -c '
  if .required_pull_request_reviews != null
  then .required_pull_request_reviews |= with_entries(select(.value != null))
  else . end')
```

I haven't re-run the workflow against a real branch.

The 422 body also complains about `required_status_checks`. That looks
like a side effect of the nulls rather than a separate rejection, since
the payload sends both `contexts` and `checks`. If the job still fails
after this change, check that field next.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
## Summary

The release workflow's "Restore release branch protection" step failed
with HTTP 422. The unlock action captured `required_status_checks` with
both `contexts` and `checks`, and the branch protection PUT API rejects
that combination. The captured settings now include only `checks`.

Not yet verified by a re-run of the release workflow.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…283)

## Summary

The "Promote PublicAPI files" job failed because
`branch-protection/unlock` called the classic branch protection API,
which returns 404 now that the repo uses rulesets.

- `unlock` now finds the repository rulesets applying to the branch and
adds it to each ruleset's `exclude` list (with rollback on failure);
outputs `ruleset-ids`.
- `lock` now takes `ruleset-ids` and removes the branch from the exclude
lists.
- `release.yml` and `backtrack.yml` updated accordingly (the old
`lock_branch` steps in backtrack were removed as rulesets have no
equivalent).
- Docs updated.

Not run end-to-end; requires `GH_ADMIN_TOKEN` with repository
administration write access.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization

Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary

Allow the Create release job to run when unrelated upstream jobs are
skipped, while requiring all direct prerequisites to succeed and
preventing execution after cancellation.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization

Co-authored-by: petesramek <2333452+petesramek@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Summary

nuget version fixed to range and wildcards

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization
)

## Summary

The `Push assembly metadata artifact` step in `build.yml` pushed
directly to a branch covered by a ruleset (e.g. `develop/**`), failing
with GH013 (required status checks / deployment). Wrap it with the
existing `branch-protection/unlock` and `lock` actions, as `release.yml`
does. Failing run: 40907507353.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#297)

…cancelled or error

## Summary

<!-- Briefly describe what this PR does and why. -->

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization
## Summary

Docs removed version 0.0, fixed build action

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation / samples only

## Checklist

- [ ] Unit tests added or updated (`/tests`)
- [ ] Benchmarks added or updated (`/benchmarks`) — if
performance-impacting
- [ ] XML doc comments updated for all public API changes
- [ ] `dotnet format` run with no issues
- [ ] README and `/samples` updated — if public API changed
- [ ] PR label added (`breaking` / `feat` / `fix` / `docs`) — for
changelog categorization
disable assembly metadata for version 0.0
The Update CHANGELOG.md job failed with GH013 (changes must go through a
PR) on release/1.0. Both the CHANGELOG and PublicAPI.Unshipped pushes
now use artifact + branch-protection unlock + git/push-changes + lock,
matching promote-api-files. Remaining pushes (tags, new branches) don't
target protected existing branches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@petesramek
petesramek enabled auto-merge (squash) October 29, 2026 19:18
@petesramek
petesramek deployed to Development October 29, 2026 19:19 — with GitHub Actions Active
@petesramek
petesramek merged commit 85f84c1 into preview/1.0 Oct 29, 2026
11 checks passed

This branch was successfully deployed

1 active deployment
Development — 103e0ef2 Deployed Oct 29, 2026 by petesramek via Publish development package #296
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants