Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
27c7cca
Implement governed release automation
Jun 1, 2026
c7a86e1
Fix governed release workflow contracts
Jun 1, 2026
8a8f1ae
Enforce governed release evidence provenance
Jun 1, 2026
ce9c6d3
Enforce governed release publish controls
Jun 1, 2026
f34107b
Tighten governed release publish checks
Jun 2, 2026
8a51515
Fail closed on postpublish verification
Jun 2, 2026
a270c2c
Fail closed on postpublish verification
Jun 2, 2026
ecfd51e
Fix release validation artifact naming
Jun 2, 2026
6387bf7
Tighten release validation artifact contract
Jun 2, 2026
ace1460
test: cover governed release provenance checks
Jun 2, 2026
c36504f
Fail closed on release hook execution
Jun 2, 2026
48af0dd
Fix governed release hook execution
Jun 2, 2026
770b445
test: cover release publish provenance checks
Jun 2, 2026
1693c31
Fix release publish evidence isolation
Jun 2, 2026
33aa8c5
Fix release asset publishing scope
Jun 2, 2026
176f794
Fix governed release asset upload scope
Jun 2, 2026
2dad69c
Fix governed release asset filtering
Jun 2, 2026
2b294b3
Fix governed release asset selection
Jun 2, 2026
e79408b
Harden release publish asset staging
Jun 2, 2026
8c432eb
Harden governed release asset staging
Jun 2, 2026
9a26371
Add release asset publish regression coverage
Jun 2, 2026
4c777be
Harden governed release asset staging
Jun 2, 2026
6df1f0d
Fix release provenance guard coverage
Jun 2, 2026
c2ba431
Tighten release publish asset guards
Jun 2, 2026
e8da8d7
Harden release asset staging isolation
Jun 2, 2026
37f5fb8
Guard release publish asset isolation
Jun 2, 2026
1add087
Clarify release validation evidence staging
Jun 2, 2026
01d150c
Harden governed release publish staging
Jun 2, 2026
9a0f641
Fix generated preflight evidence path
Jun 2, 2026
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
63 changes: 63 additions & 0 deletions .github/ISSUE_TEMPLATE/release_train.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Release Train
about: Governed release train for an OMT-Global repository
title: "Release: vX.Y.Z"
labels:
- release:train
- review:release
body:
- type: input
id: version
attributes:
label: Version
placeholder: v1.2.3 or v1.2.3-rc.1
validations:
required: true
- type: dropdown
id: channel
attributes:
label: Channel
options:
- rc
- beta
- stable
- maintenance
validations:
required: true
- type: input
id: release_branch
attributes:
label: Release branch
placeholder: release/1.2
- type: input
id: target_sha
attributes:
label: Target SHA
placeholder: Full commit SHA for the release candidate
- type: textarea
id: scope
attributes:
label: Scope
description: User-facing changes, fixes, risks, exclusions.
validations:
required: true
- type: textarea
id: gates
attributes:
label: Gates
value: |
- [ ] Release branch created
- [ ] Scope locked
- [ ] Changelog/release notes prepared
- [ ] Version surfaces updated
- [ ] Preflight passed
- [ ] preflight_run_id recorded:
- [ ] Full validation passed
- [ ] validation_run_id recorded:
- [ ] Exact tag created
- [ ] Publish approval granted
- [ ] Artifacts published
- [ ] GitHub Release created or updated
- [ ] Release evidence uploaded
- [ ] Postpublish verification passed
- [ ] Floating tags/channels promoted if applicable
- [ ] Release issue closed
27 changes: 27 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by OMT Bootstrap. Keep dependency policy in project.bootstrap.yaml.
# Dependabot alerts + security updates are managed through GitHub security settings;
# this file governs routine scheduled version update PRs.
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
npm-minor-patch:
update-types:
- "minor"
- "patch"
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-major"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-major"
60 changes: 2 additions & 58 deletions .github/workflows/ai-attestation-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,42 +30,14 @@ permissions:

jobs:
attest:
runs-on:
- self-hosted
- linux
- shell-only
- public
runs-on: ubuntu-latest
outputs:
sha: ${{ steps.meta.outputs.sha }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Ensure envsubst is available
shell: bash
run: |
set -euo pipefail
if command -v envsubst >/dev/null 2>&1; then
exit 0
fi

if ! command -v apt-get >/dev/null 2>&1; then
echo "envsubst is required by cosign-installer, but apt-get is unavailable." >&2
exit 1
fi

if [[ "$(id -u)" == "0" ]]; then
apt-get update
apt-get install -y --no-install-recommends gettext-base
elif command -v sudo >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y --no-install-recommends gettext-base
else
echo "envsubst is missing and this runner cannot install gettext-base." >&2
exit 1
fi

- name: Install cosign
uses: sigstore/cosign-installer@v4.1.1

Expand Down Expand Up @@ -117,37 +89,9 @@ jobs:
retention-days: ${{ inputs.retention_days }}

verify:
runs-on:
- self-hosted
- linux
- shell-only
- public
runs-on: ubuntu-latest
needs: attest
steps:
- name: Ensure envsubst is available
shell: bash
run: |
set -euo pipefail
if command -v envsubst >/dev/null 2>&1; then
exit 0
fi

if ! command -v apt-get >/dev/null 2>&1; then
echo "envsubst is required by cosign-installer, but apt-get is unavailable." >&2
exit 1
fi

if [[ "$(id -u)" == "0" ]]; then
apt-get update
apt-get install -y --no-install-recommends gettext-base
elif command -v sudo >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y --no-install-recommends gettext-base
else
echo "envsubst is missing and this runner cannot install gettext-base." >&2
exit 1
fi

- name: Install cosign
uses: sigstore/cosign-installer@v4.1.1

Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/full-release-validation-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Reusable Full Release Validation

on:
workflow_call:
inputs:
target_ref: { required: true, type: string }
release_profile: { required: true, type: string }
runs_on: { required: false, type: string, default: '["ubuntu-latest"]' }
validate_script: { required: false, type: string, default: scripts/release/validate.sh }
artifact_dir: { required: false, type: string, default: dist/release }
evidence_artifact_name: { required: false, type: string, default: release-evidence }
evidence_retention_days: { required: false, type: number, default: 365 }

permissions:
contents: read
actions: read

jobs:
validate:
runs-on: ${{ fromJSON(inputs.runs_on || '["ubuntu-latest"]') }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.target_ref }}
fetch-depth: 0
- name: Run release validation
env:
TARGET_REF: ${{ inputs.target_ref }}
RELEASE_PROFILE: ${{ inputs.release_profile }}
VALIDATE_SCRIPT: ${{ inputs.validate_script }}
ARTIFACT_DIR: ${{ inputs.artifact_dir }}
run: |
set -euo pipefail
mkdir -p "$ARTIFACT_DIR"
validate_status=skipped
standard_status=skipped
if [[ -x "$VALIDATE_SCRIPT" ]]; then
"$VALIDATE_SCRIPT"
validate_status=passed
fi
if [[ -f package.json ]] && node -e "const p=require('./package.json'); process.exit(p.scripts?.check ? 0 : 1)" >/dev/null 2>&1; then
npm run check
standard_status=passed
fi
target_sha="$(git rev-parse HEAD)"
cat >"$ARTIFACT_DIR/validation-evidence.json" <<JSON
{"schema_version":1,"repo":"${GITHUB_REPOSITORY}","target_ref":"${TARGET_REF}","target_sha":"${target_sha}","validation_run_id":"${GITHUB_RUN_ID}","release_profile":"${RELEASE_PROFILE}","checks":{"validate_script":"${validate_status}","standard_checks":"${standard_status}"}}
JSON
- uses: actions/upload-artifact@v4
with:
name: ${{ inputs.evidence_artifact_name }}-validation
path: ${{ inputs.artifact_dir }}/validation-evidence.json
retention-days: ${{ inputs.evidence_retention_days }}
26 changes: 26 additions & 0 deletions .github/workflows/full-release-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Full Release Validation

on:
workflow_dispatch:
inputs:
target_ref:
description: Branch, tag, or full SHA to validate
required: true
type: string
release_profile:
description: Validation depth
required: true
default: standard
type: choice
options: [smoke, standard, full]

jobs:
validate:
uses: OMT-Global/bootstrap/.github/workflows/full-release-validation-reusable.yml@refs/heads/main
with:
target_ref: ${{ inputs.target_ref }}
release_profile: ${{ inputs.release_profile }}
validate_script: scripts/release/validate.sh
artifact_dir: dist/release
evidence_artifact_name: release-evidence
evidence_retention_days: 365
52 changes: 52 additions & 0 deletions .github/workflows/release-postpublish-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Reusable Release Postpublish

on:
workflow_call:
inputs:
tag: { required: true, type: string }
channel: { required: true, type: string }
release_issue: { required: false, type: string, default: "" }
postpublish_script: { required: false, type: string, default: scripts/release/postpublish.sh }
artifact_dir: { required: false, type: string, default: dist/release }
evidence_artifact_name: { required: false, type: string, default: release-evidence }

permissions:
contents: read
actions: read

jobs:
postpublish:
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
- name: Verify published release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
CHANNEL: ${{ inputs.channel }}
RELEASE_ISSUE: ${{ inputs.release_issue }}
POSTPUBLISH_SCRIPT: ${{ inputs.postpublish_script }}
ARTIFACT_DIR: ${{ inputs.artifact_dir }}
run: |
set -euo pipefail
mkdir -p "$ARTIFACT_DIR"
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null
asset_count="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets | length')"
[[ "$asset_count" -ge 1 ]] || { echo "Release has no assets." >&2; exit 1; }
if [[ -x "$POSTPUBLISH_SCRIPT" ]]; then
"$POSTPUBLISH_SCRIPT" "$TAG"
else
echo "Postpublish verification hook is missing or not executable: $POSTPUBLISH_SCRIPT" >&2
exit 1
fi
printf '{"schema_version":1,"repo":"%s","tag":"%s","channel":"%s","release_issue":"%s","postpublish_run_id":"%s","release_assets":%s}\n' "$GITHUB_REPOSITORY" "$TAG" "$CHANNEL" "$RELEASE_ISSUE" "$GITHUB_RUN_ID" "$asset_count" >"$ARTIFACT_DIR/postpublish-evidence.json"
- uses: actions/upload-artifact@v4
with:
name: ${{ inputs.evidence_artifact_name }}-postpublish
path: ${{ inputs.artifact_dir }}/postpublish-evidence.json
30 changes: 30 additions & 0 deletions .github/workflows/release-postpublish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Release Postpublish

on:
workflow_dispatch:
inputs:
tag:
description: Exact release tag to verify
required: true
type: string
channel:
description: Release channel
required: true
default: stable
type: choice
options: [rc, beta, stable, maintenance]
release_issue:
description: Release issue number
required: false
type: string

jobs:
postpublish:
uses: OMT-Global/bootstrap/.github/workflows/release-postpublish-reusable.yml@refs/heads/main
with:
tag: ${{ inputs.tag }}
channel: ${{ inputs.channel }}
release_issue: ${{ inputs.release_issue }}
postpublish_script: scripts/release/postpublish.sh
artifact_dir: dist/release
evidence_artifact_name: release-evidence
Loading
Loading