Skip to content
Open
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
52 changes: 0 additions & 52 deletions .cursor/rules/zz_generated.base-llm-rules.mdc

This file was deleted.

100 changes: 97 additions & 3 deletions .github/workflows/zz_generated.add-to-project-board.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,113 @@ jobs:
permission-issues: read
permission-organization-projects: write
- name: Add issue to team board
id: add-to-board
if: ${{ env.BOARD != 'null' && env.BOARD != '' && env.BOARD != null }}
uses: actions/add-to-project@f2c1f1fa649a891be84948ea89b5dfba1526ef05 # v2.0.1
with:
project-url: ${{ env.BOARD }}
github-token: ${{ steps.app-token.outputs.token }}
# The roadmap board's Team field is a single select whose options carry
# emoji ("Honey Badger 🦡"); a label matches the option with the same
# letters, so team/honeybadger and team/honey-badger both match.
- name: Set the roadmap Team field from the team label
if: >-
steps.add-to-board.outputs.itemId != '' &&
env.BOARD == 'https://github.com/orgs/giantswarm/projects/273' &&
startsWith(github.event.label.name, 'team/') &&
github.event.label.name != 'team/null'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
ITEM_ID: ${{ steps.add-to-board.outputs.itemId }}
LABEL: ${{ github.event.label.name }}
run: |
set -euo pipefail
team=$(printf '%s' "${LABEL#team/}" | tr -cd '[:alpha:]' | tr '[:upper:]' '[:lower:]')
read -r project field option < <(gh api graphql -F number=273 -f query='
query($number: Int!) {
organization(login: "giantswarm") {
projectV2(number: $number) {
id
field(name: "Team") { ... on ProjectV2SingleSelectField { id options { id name } } }
}
}
}' | jq -r --arg team "$team" '.data.organization.projectV2 as $p
| ($p.field.options[] | select((.name | ascii_downcase | gsub("[^a-z]"; "")) == $team) | .id) as $o
| [$p.id, $p.field.id, $o] | @tsv') || true
if [ -z "${option:-}" ]; then
echo "::warning::No roadmap Team option matches ${LABEL}"
exit 0
fi
gh api graphql -f project="$project" -f item="$ITEM_ID" -f field="$field" -f option="$option" -f query='
mutation($project: ID!, $item: ID!, $field: ID!, $option: String!) {
updateProjectV2ItemFieldValue(input: {projectId: $project, itemId: $item, fieldId: $field, value: {singleSelectOptionId: $option}}) {
projectV2Item { id }
}
}' > /dev/null
echo "Team set from ${LABEL}"

# The repository's owning team is the single team of the CODEOWNERS default
# rule, which align-files writes from the team file that declares it. Its
# label, added as the App, fires `labeled`, and add_to_team_board puts the
# issue on the team's board. An issue opened with a team label keeps it.
add_owner_team_label:
name: Add the owning team's label to a new issue
runs-on: ubuntu-latest
if: >-
github.event.action == 'opened' &&
!contains(join(github.event.issue.labels.*.name, ' '), 'team/')
permissions:
contents: read
outputs:
team: ${{ steps.owner.outputs.team }}
steps:
- name: Find the owning team in CODEOWNERS
id: owner
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
codeowners=""
for path in .github/CODEOWNERS CODEOWNERS docs/CODEOWNERS; do
if codeowners=$(gh api -H "Accept: application/vnd.github.raw" "repos/${GITHUB_REPOSITORY}/contents/${path}" 2>/dev/null); then
break
fi
codeowners=""
done
# The last matching rule wins, as GitHub reads CODEOWNERS.
rule=$(printf '%s\n' "$codeowners" | sed 's/#.*//' | awk '$1 == "*"' | tail -n 1)
teams=$(printf '%s\n' "$rule" | grep -oE '@giantswarm/team-[a-z0-9-]+' | sed 's|@giantswarm/team-||' | sort -u || true)
if [ "$(printf '%s' "$teams" | grep -c .)" = 1 ]; then
echo "Owning team: ${teams}"
echo "team=${teams}" >> "$GITHUB_OUTPUT"
else
echo "No single owning team in CODEOWNERS"
fi
- name: Mint App token for the label
id: app-token
if: steps.owner.outputs.team != ''
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ secrets.PROJECT_AUTOMATION_CLIENT_ID }}
private-key: ${{ secrets.PROJECT_AUTOMATION_PRIVATE_KEY }}
permission-issues: write
- name: Apply the owning team's label
if: steps.owner.outputs.team != ''
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
LABEL: team/${{ steps.owner.outputs.team }}
ISSUE: ${{ github.event.issue.number }}
run: gh api "repos/${GITHUB_REPOSITORY}/issues/${ISSUE}/labels" -f "labels[]=${LABEL}" > /dev/null

# An issue opened with a label or an assignee also fires `labeled` or
# `assigned`, which add it above; only a bare one is added here.
# A repository without a single owning team: a bare issue still reaches the
# roadmap board's inbox. An issue opened with a label or an assignee also
# fires `labeled` or `assigned`, which add it above.
add_unlabelled_to_roadmap_board:
name: Add unlabelled, unassigned issue to roadmap board
runs-on: ubuntu-latest
needs: add_owner_team_label
if: >-
github.event.action == 'opened' &&
needs.add_owner_team_label.outputs.team == '' &&
join(github.event.issue.labels.*.name, '') == '' &&
join(github.event.issue.assignees.*.login, '') == ''
steps:
Expand Down
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!-- BEGIN devctl-managed: edits in this block are overwritten -->
<!-- markdownlint-disable-file MD041 -->

## Giant Swarm conventions

devctl maintains this block from [its template][devctl-agents-block] and
overwrites changes to it. Instructions for this repository go in the section
below. Where they differ from this block, they take precedence.

[devctl-agents-block]: https://github.com/giantswarm/devctl/blob/main/pkg/gen/input/llm/internal/file/agents_block.md.template

- Never put customer names or other customer information in code, commit
messages, issues or pull requests. Most Giant Swarm repositories are public,
and private ones are kept ready to become public.
- Giant Swarm's style guides are in <https://github.com/giantswarm/fmt>, one
directory per topic (go, helm, kubernetes, prometheus, …). Read the relevant
guide before you write or review that kind of code.
- Use conventional commits for commit messages and pull request titles.
- If `.github/workflows/zz_generated.auto_release.yaml` exists, GitHub Releases
are written from pull request titles: do not edit CHANGELOG.md. Otherwise,
add your change to CHANGELOG.md as described in
<https://github.com/giantswarm/fmt/blob/main/releases/changelog.md>.
- If `.pre-commit-config.yaml` exists, run
`pre-commit run --files <changed files>` before committing; not every clone
has the hooks installed.
- Do not add linters, formatters or other tooling unless asked; use what the
repository already has.
- Put documentation in `docs/` rather than growing README.md, which gives an
overview and links into `docs/`. When a change affects documented behaviour,
update the documentation in the same pull request.
- If the skills of the `giantswarm/claude-code` plugin marketplace are not
available to you and the user works at Giant Swarm, tell them once that
they can install the `gs-base`, `gs-dev` and `gs-appdev`
plugins from it.

<!-- END devctl-managed -->

## This repository
18 changes: 13 additions & 5 deletions Makefile.gen.app.mk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# devctl
#
# https://github.com/giantswarm/devctl/blob/bbc57913962860b1f2b3524a74a9692931c58034/pkg/gen/input/makefile/internal/file/Makefile.gen.app.mk.template
# https://github.com/giantswarm/devctl/blob/322af9d3c53b7b163c5f162f1f521273b5d723fb/pkg/gen/input/makefile/internal/file/Makefile.gen.app.mk.template
#

##@ App
Expand Down Expand Up @@ -88,6 +88,10 @@ HELM_UNITTEST_RETRY_DELAY ?= 5
# repository: its names render only once a repository created from it fills them in, so
# its suites cannot pass here and run on the rendered copy instead. It is skipped, as a
# chart reference with a placeholder is no chart for the catalog either.
# A wrapper chart's suites render its upstream subchart, and most repositories gitignore
# `charts/*.tgz`, so a fresh clone (CI) has none. A chart with a missing dependency gets
# `helm dependency update` before its suites run: unlike `helm dependency build`, it needs
# no `helm repo add`. A chart whose dependencies are present is not touched.
helm-test: install-helm-unittest ## Run the chart unit tests (helm-unittest).
@echo "====> $@"
@found=""; \
Expand All @@ -97,6 +101,9 @@ helm-test: install-helm-unittest ## Run the chart unit tests (helm-unittest).
case "$$c" in *[{}]*) echo "skipping $$c: a template's placeholder chart"; continue;; esac; \
ls "$$d"/*_test.yaml >/dev/null 2>&1 || continue; \
found=1; \
if helm dependency list "$$c" 2>/dev/null | grep -q 'missing$$'; then \
helm dependency update "$$c" || exit 1; \
fi; \
helm unittest -f "$$(basename "$$d")/*_test.yaml" --with-subchart=false "$$c" || exit 1; \
done; \
[ -n "$$found" ] || echo "no chart unit tests under helm/"
Expand All @@ -110,19 +117,20 @@ helm-test: install-helm-unittest ## Run the chart unit tests (helm-unittest).
# network error (a connection reset) fails it; the install runs up to
# HELM_UNITTEST_ATTEMPTS times, HELM_UNITTEST_RETRY_DELAY seconds apart. A failed attempt
# can leave a half-installed plugin behind that the next install refuses, so it is
# uninstalled before each retry.
# uninstalled after every failure, including the last. A matching version must also
# run its binary: plugin metadata alone survives an interrupted download.
install-helm-unittest:
@have=$$(helm plugin list 2>/dev/null | awk '$$1 == "unittest" {print $$2}'); \
want=$$(echo "$(HELM_UNITTEST_VERSION)" | sed 's/^v//'); \
if [ "$$have" != "$$want" ]; then \
if [ "$$have" != "$$want" ] || ! helm unittest --version >/dev/null 2>&1; then \
[ -z "$$have" ] || helm plugin uninstall unittest >/dev/null 2>&1 || true; \
verify=""; \
helm plugin install --help 2>&1 | grep -q -e '--verify' && verify="--verify=false"; \
attempt=1; \
until helm plugin install $$verify https://github.com/helm-unittest/helm-unittest --version $(HELM_UNITTEST_VERSION); do \
helm plugin uninstall unittest >/dev/null 2>&1 || true; \
[ "$$attempt" -lt "$(HELM_UNITTEST_ATTEMPTS)" ] || exit 1; \
echo "helm-unittest install failed (attempt $$attempt of $(HELM_UNITTEST_ATTEMPTS)), retrying in $(HELM_UNITTEST_RETRY_DELAY)s" >&2; \
helm plugin uninstall unittest >/dev/null 2>&1 || true; \
sleep $(HELM_UNITTEST_RETRY_DELAY); \
attempt=$$((attempt + 1)); \
done; \
Expand All @@ -140,7 +148,7 @@ test: helm-test
# pins aligned with gen circleci; repositories with explicit CI pins override them
# in Makefile.custom.mk. This target supports ATS 1.x (the generated CI default).
# renovate: datasource=docker depName=gsoci.azurecr.io/giantswarm/app-test-suite
ATS_VERSION ?= 1.0.3
ATS_VERSION ?= 1.1.1
# renovate: datasource=docker depName=gsoci.azurecr.io/giantswarm/kind-node
ATS_KIND_NODE_IMAGE_VERSION ?= v1.37.0
ATS_KIND_NODE_IMAGE ?= gsoci.azurecr.io/giantswarm/kind-node:$(ATS_KIND_NODE_IMAGE_VERSION)
Expand Down