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
24 changes: 0 additions & 24 deletions .github/workflows/build-and-push-app.yaml.inactive

This file was deleted.

71 changes: 31 additions & 40 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: "[CI] Lint, build & publish"

# Readable run titles in the Actions UI: PR runs show the PR number and
# title; main pushes show the head commit subject.
# PR runs show PR title; main runs show head commit subject.
run-name: >-
${{ github.event_name == 'pull_request'
&& format('PR #{0} — {1}', github.event.pull_request.number, github.event.pull_request.title)
Expand All @@ -16,18 +15,12 @@ on:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

# Least-privilege default: only read access at the workflow level.
# Jobs that need more elevate locally.
# Default to least privilege; jobs elevate only when needed.
permissions:
contents: read

jobs:
# ── Quality gates ─────────────────────────────────────────
#
# Lint, test, and Helm lint run on every push + PR as parallel
# quality gates. Their failure breaks the workflow status but
# does not block the image build (surfaced via branch protection
# required status checks).
# Quality gates

test:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -66,16 +59,7 @@ jobs:
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-ignore-missing-schemas

# ── Container image ──────────────────────────────────────
#
# The shared reusable workflow handles checkout, change
# detection, Docker build/push, Cosign signing, and Trivy
# scanning.
#
# `helm-lint` is intentionally NOT in `needs` — it runs as an
# independent parallel job whose failure surfaces via branch
# protection (required status check) rather than delaying or
# blocking image builds.
# Container image build/publish (reusable workflow)

build-open-data:
uses: bcit-tlu/.github/.github/workflows/oci-build.yaml@main
Expand All @@ -92,12 +76,7 @@ jobs:
tag_prefix: "v"
secrets: inherit

# ── Helm chart → OCI registry (main push) ─────────────────
#
# Publishes the chart with the same RC version that the image
# received so the chart and app tags stay in lockstep. Only
# runs on main pushes when the component actually changed.
# Release-time chart publishing is handled by helm-publish.yaml.
# Publish chart from main pushes using the image RC version.

helm-publish:
needs: [helm-lint, build-open-data]
Expand All @@ -119,30 +98,38 @@ jobs:
- uses: sigstore/cosign-installer@v3

- name: Cosign login (OCI)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: ${{ env.REGISTRY }}
ACTOR: ${{ github.actor }}
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | \
cosign login "${{ env.REGISTRY }}" \
-u "${{ github.actor }}" \
echo "${GITHUB_TOKEN}" | \
cosign login "${REGISTRY}" \
-u "${ACTOR}" \
--password-stdin

- name: Helm login (OCI)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: ${{ env.REGISTRY }}
ACTOR: ${{ github.actor }}
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | \
helm registry login "${{ env.REGISTRY }}" \
-u "${{ github.actor }}" \
echo "${GITHUB_TOKEN}" | \
helm registry login "${REGISTRY}" \
-u "${ACTOR}" \
--password-stdin

- name: Package, push & sign chart
shell: bash
env:
VERSION: ${{ needs.build-open-data.outputs.rc_version }}
OCI_BASE: oci://${{ env.REGISTRY }}/${{ github.repository }}/charts
IMAGE_BASE: ${{ env.REGISTRY }}/${{ github.repository }}/charts
run: |
set -euo pipefail

OCI_BASE="oci://${{ env.REGISTRY }}/${{ github.repository }}/charts"
IMAGE_BASE="${{ env.REGISTRY }}/${{ github.repository }}/charts"
CHART_DIR="charts"
CHART_NAME=$(awk '/^name:/{print $2; exit}' "${CHART_DIR}/Chart.yaml")
CHART_NAME=$(yq '.name' "${CHART_DIR}/Chart.yaml")
DEST_DIR="/tmp/charts"
mkdir -p "${DEST_DIR}"

Expand All @@ -153,11 +140,15 @@ jobs:

PUSH_OUT=$(helm push \
"${DEST_DIR}/${CHART_NAME}-${VERSION}.tgz" \
"${OCI_BASE}" 2>&1) || true
"${OCI_BASE}" 2>&1) || { echo "${PUSH_OUT}"; exit 1; }
echo "${PUSH_OUT}"
printf '%s\n' "${PUSH_OUT}" | grep -q '^Digest:' || { echo "::error::helm push failed for ${CHART_NAME}"; exit 1; }

DIGEST=$(printf '%s\n' "${PUSH_OUT}" | awk '/^Digest:/{print $2}')
if [[ -n "${DIGEST}" ]]; then
cosign sign --yes "${IMAGE_BASE}/${CHART_NAME}@${DIGEST}"
# Tolerant digest parse: case-insensitive, allows leading whitespace.
DIGEST=$(printf '%s\n' "${PUSH_OUT}" \
| awk 'tolower($1)=="digest:"{print $2; exit}')
if [[ -z "${DIGEST}" ]]; then
echo "::error::helm push for ${CHART_NAME} succeeded but no digest found in output (helm output format may have changed)"
exit 1
fi

cosign sign --yes "${IMAGE_BASE}/${CHART_NAME}@${DIGEST}"
47 changes: 34 additions & 13 deletions .github/workflows/helm-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ permissions:

env:
REGISTRY: ghcr.io
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
publish:
Expand Down Expand Up @@ -46,31 +47,42 @@ jobs:

- name: Cosign login (OCI)
if: steps.parse.outputs.publish == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: ${{ env.REGISTRY }}
ACTOR: ${{ github.actor }}
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | \
cosign login "${{ env.REGISTRY }}" \
-u "${{ github.actor }}" \
echo "${GITHUB_TOKEN}" | \
cosign login "${REGISTRY}" \
-u "${ACTOR}" \
--password-stdin

- name: Helm login (OCI)
if: steps.parse.outputs.publish == 'true'
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: ${{ env.REGISTRY }}
ACTOR: ${{ github.actor }}
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | \
helm registry login "${{ env.REGISTRY }}" \
-u "${{ github.actor }}" \
echo "${GITHUB_TOKEN}" | \
helm registry login "${REGISTRY}" \
-u "${ACTOR}" \
--password-stdin

- name: Package and push chart
id: push
if: steps.parse.outputs.publish == 'true'
shell: bash
env:
VERSION: ${{ steps.parse.outputs.version }}
OCI_REPO: oci://${{ env.REGISTRY }}/${{ github.repository }}/charts
run: |
set -euo pipefail

VERSION="${{ steps.parse.outputs.version }}"
CHART_DIR="charts"
DEST_DIR="/tmp/charts"
CHART_NAME=$(yq '.name' "${CHART_DIR}/Chart.yaml")

mkdir -p "${DEST_DIR}"

Expand All @@ -80,16 +92,25 @@ jobs:
-d "${DEST_DIR}"

PUSH_OUT=$(helm push \
"${DEST_DIR}/open-data-${VERSION}.tgz" \
"oci://${{ env.REGISTRY }}/${{ github.repository }}/charts" 2>&1) || true
"${DEST_DIR}/${CHART_NAME}-${VERSION}.tgz" \
"${OCI_REPO}" 2>&1) || { echo "${PUSH_OUT}"; exit 1; }
echo "${PUSH_OUT}"
printf '%s\n' "${PUSH_OUT}" | grep -q '^Digest:' || { echo '::error::helm push failed'; exit 1; }

DIGEST=$(printf '%s\n' "${PUSH_OUT}" | awk '/^Digest:/{print $2}')
# Tolerant digest parse: case-insensitive, allows leading whitespace.
DIGEST=$(printf '%s\n' "${PUSH_OUT}" \
| awk 'tolower($1)=="digest:"{print $2; exit}')
if [[ -z "${DIGEST}" ]]; then
echo "::error::helm push succeeded but no digest found in output (helm output format may have changed)"
exit 1
fi

echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
echo "chart_name=${CHART_NAME}" >> "$GITHUB_OUTPUT"

- name: Sign chart
if: steps.parse.outputs.publish == 'true' && steps.push.outputs.digest != ''
env:
CHART_REF: ${{ env.REGISTRY }}/${{ github.repository }}/charts/${{ steps.push.outputs.chart_name }}
DIGEST: ${{ steps.push.outputs.digest }}
run: |
cosign sign --yes \
"${{ env.REGISTRY }}/${{ github.repository }}/charts/open-data@${{ steps.push.outputs.digest }}"
cosign sign --yes "${CHART_REF}@${DIGEST}"
30 changes: 15 additions & 15 deletions .github/workflows/pr-title-lint.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: pr-title-lint

# Gate non-conventional PR titles at the merge boundary so unparseable
# squash-merge commits can't land on main and silently starve
# release-please of the feat:/fix:/BREAKING signals it uses to open
# release PRs.
# Enforce Conventional Commit PR titles so release-please can parse changes.

on:
pull_request_target:
types: [opened, edited, reopened, synchronize]
branches: [main]

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

permissions:
contents: read
pull-requests: write
Expand Down Expand Up @@ -49,25 +49,25 @@ jobs:
with:
header: pr-title-lint
message: |
## PR title does not match Conventional Commits

**Why this matters (tl;dr)** — release-please reads PR titles on `main` to decide whether to cut a release, what version bump to apply, and how to group CHANGELOG entries. A title that doesn't parse is silently dropped; no release PR opens, no error, workflow still exits 0.
## PR title must follow Conventional Commits

**Required structure**
release-please uses PR titles on `main` to determine version bumps and changelog entries.

```
<type>[optional scope][!]: <subject>
```

- `<type>` — `feat` / `fix` / `docs` / `style` / `refactor` / `perf` / `test` / `build` / `ci` / `chore` / `revert`
- `<subject>` — lowercase first letter, imperative mood, no trailing period
- add `!` (or `BREAKING CHANGE:` in the body) for breaking changes

**Good** — `feat: add search page`, `fix: correct broken nav link`, `ci: tighten workflow permissions`
- `type`: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
- `subject`: start lowercase, imperative mood, no trailing period
- breaking change: add `!` (or `BREAKING CHANGE:` in body)

**Bad** — `Add search page` (no type), `feat: Add search page` (uppercase subject)
Examples:
- ✅ `feat: add search page`
- ✅ `fix: correct broken nav link`
- ❌ `Add search page`
- ❌ `feat: Add search page`

Edit the PR title in the GitHub UI — the check re-runs automatically within seconds, no push needed.
Edit the PR title in GitHub; this check reruns automatically.

- name: Clear failure explainer on success
if: success() && steps.lint.outcome == 'success'
Expand Down
24 changes: 8 additions & 16 deletions .github/workflows/release-please.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ on:
push:
branches: [main]

# Least-privilege default: only `checkout` needs read access.
# Each job elevates permissions to just what that job requires.
# Default to least privilege; jobs elevate only when needed.
permissions:
contents: read

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
release:
runs-on: ubuntu-latest
Expand All @@ -25,17 +27,8 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}

# `release-as` per-package is a one-shot: once the release PR it
# triggered has merged and the manifest has caught up, the override
# must be removed, otherwise every subsequent release-please run
# keeps pinning the same version and blocks normal semver bumps
# from feat:/fix: commits. This guard runs *after* release-please
# and is skipped whenever `releases_created=true`, so the
# tag/release finalization that happens on the merge-of-chore-PR
# push (where manifest has just caught up to release-as) isn't
# blocked. On any subsequent push where the state is still stale
# and there's nothing to release, the workflow fails loudly to
# force the cleanup PR.
# `release-as` is one-shot. Once manifest catches up, remove it;
# otherwise future semver bumps are pinned and releases stall.
- uses: actions/checkout@v6
if: steps.rp.outputs.releases_created != 'true'
- name: Guard against stale `release-as` entries
Expand All @@ -59,9 +52,8 @@ jobs:
sys.exit(1)
PY

# Releases created via GITHUB_TOKEN do NOT trigger workflows that listen
# on `release: published` or `push: tags:`. Dispatch helm-publish and
# release-retag explicitly for the released tag.
# Releases made with GITHUB_TOKEN do not trigger release/tag workflows.
# Dispatch publish workflows explicitly for the new tag.
dispatch-publish:
needs: release
if: needs.release.outputs.releases_created == 'true'
Expand Down
Loading
Loading