From f3a731360896349a7676122dc7553f275bcc96f4 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 29 Sep 2026 19:35:23 +0200 Subject: [PATCH 1/7] Install tools under RUNNER_TEMP instead of with sudo mv A binary moved into /usr/local/bin needs root, which a hosted runner reaches through sudo and a container job has no business carrying. RUNNER_TEMP belongs to the job everywhere, and GITHUB_PATH puts it on the path of the steps after the install. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01McrBgv5WFPJt215Qt8MXLn --- actions/cosign/sign/action.yml | 6 +++++- actions/syft/generate-sbom/action.yml | 6 +++++- actions/terraform/setup/action.yml | 6 +++++- actions/tflint/setup/action.yml | 6 +++++- actions/trivy/scan/action.yml | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/actions/cosign/sign/action.yml b/actions/cosign/sign/action.yml index 3316f46..a6bb739 100644 --- a/actions/cosign/sign/action.yml +++ b/actions/cosign/sign/action.yml @@ -42,7 +42,11 @@ runs: echo "Verification successful!" rm $COSIGN_CHECKSUM_FILE chmod +x $COSIGN_BINARY - sudo mv $COSIGN_BINARY /usr/local/bin/cosign + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv $COSIGN_BINARY "${RUNNER_TEMP}/bin/cosign" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" cosign version else echo "ERROR: Checksum verification failed!" >&2 diff --git a/actions/syft/generate-sbom/action.yml b/actions/syft/generate-sbom/action.yml index c4872ba..c5b05dc 100644 --- a/actions/syft/generate-sbom/action.yml +++ b/actions/syft/generate-sbom/action.yml @@ -58,7 +58,11 @@ runs: echo "Verification successful!" tar -xzf "${SYFT_TARBALL}" syft chmod +x syft - sudo mv syft /usr/local/bin/syft + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv syft "${RUNNER_TEMP}/bin/syft" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" rm -f "${SYFT_TARBALL}" "${SYFT_CHECKSUMS}" syft version else diff --git a/actions/terraform/setup/action.yml b/actions/terraform/setup/action.yml index 21d6590..ac7abda 100644 --- a/actions/terraform/setup/action.yml +++ b/actions/terraform/setup/action.yml @@ -33,7 +33,11 @@ runs: echo "Verification successful!" unzip -o "${TERRAFORM_ZIP}" chmod +x terraform - sudo mv terraform /usr/local/bin/terraform + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv terraform "${RUNNER_TEMP}/bin/terraform" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" rm -f "${TERRAFORM_ZIP}" "${TERRAFORM_SHA256SUMS}" terraform version else diff --git a/actions/tflint/setup/action.yml b/actions/tflint/setup/action.yml index 770a67b..ab3ef82 100644 --- a/actions/tflint/setup/action.yml +++ b/actions/tflint/setup/action.yml @@ -32,7 +32,11 @@ runs: echo "Verification successful!" unzip -o "${TFLINT_ZIP}" chmod +x tflint - sudo mv tflint /usr/local/bin/tflint + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv tflint "${RUNNER_TEMP}/bin/tflint" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" rm -f "${TFLINT_ZIP}" checksums.txt tflint --version else diff --git a/actions/trivy/scan/action.yml b/actions/trivy/scan/action.yml index 98d7286..52c1e26 100644 --- a/actions/trivy/scan/action.yml +++ b/actions/trivy/scan/action.yml @@ -65,7 +65,11 @@ runs: echo "Verification successful!" tar -xzf "${TRIVY_TARBALL}" trivy chmod +x trivy - sudo mv trivy /usr/local/bin/trivy + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv trivy "${RUNNER_TEMP}/bin/trivy" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" rm -f "${TRIVY_TARBALL}" "${TRIVY_CHECKSUMS}" trivy version else From faae4aedb903fec265f5a578275fe86825a6f5b7 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 29 Sep 2026 19:43:38 +0200 Subject: [PATCH 2/7] Put .NET global tools on the path of the steps after, for any user The export only lasted for its own step, and named the home of root. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01McrBgv5WFPJt215Qt8MXLn --- actions/dotnet/install-lint-restore/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/dotnet/install-lint-restore/action.yml b/actions/dotnet/install-lint-restore/action.yml index a595889..c1fa6ee 100644 --- a/actions/dotnet/install-lint-restore/action.yml +++ b/actions/dotnet/install-lint-restore/action.yml @@ -21,7 +21,7 @@ runs: - name: Install .NET tools run: | dotnet tool install --global dotnet-reportgenerator-globaltool - export PATH="$PATH:/root/.dotnet/tools" + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" shell: bash - name: Restore .NET packages run: dotnet restore From 76c70c1e99ffa03de2c35a4fb75ac91b635a18e3 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 29 Sep 2026 19:45:00 +0200 Subject: [PATCH 3/7] Replace the FOSSA and Docker third party actions FOSSA is a composite action here, installing a pinned CLI release after checking its checksum, as Trivy and Syft already are. Login, Buildx, QEMU and the build are the Docker CLI of the runner, and the gha cache backend, which only a JavaScript action can reach, goes with them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01McrBgv5WFPJt215Qt8MXLn --- .../reusable-container-publication.yml | 39 ++++---- .github/workflows/reusable-dotnet-quality.yml | 9 +- actions/fossa/analyze/action.yml | 91 +++++++++++++++++++ 3 files changed, 114 insertions(+), 25 deletions(-) create mode 100644 actions/fossa/analyze/action.yml diff --git a/.github/workflows/reusable-container-publication.yml b/.github/workflows/reusable-container-publication.yml index cdad238..396388d 100644 --- a/.github/workflows/reusable-container-publication.yml +++ b/.github/workflows/reusable-container-publication.yml @@ -107,28 +107,31 @@ jobs: repository: devpro/github-workflow-parts ref: ${{ inputs.workflow-parts-version }} path: workflow-parts + # The Docker CLI of the runner does what the docker/* actions wrapped, with no third party action in between. - name: Login to container registry - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee - with: - registry: ${{ inputs.container-registry }} - username: ${{ secrets.container-registry-username }} - password: ${{ secrets.container-registry-password }} + env: + REGISTRY: ${{ inputs.container-registry }} + REGISTRY_USERNAME: ${{ secrets.container-registry-username }} + REGISTRY_PASSWORD: ${{ secrets.container-registry-password }} + run: echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" --username "$REGISTRY_USERNAME" --password-stdin - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 + # Emulation is only needed for a platform other than the runner's own. + if: ${{ inputs.image-platform != 'linux/amd64' }} + run: docker run --privileged --rm tonistiigi/binfmt --install all - name: Set up Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 + run: docker buildx create --use --driver docker-container - name: Build and push container image - id: build-push - uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf - with: - context: ${{ inputs.working-directory }} - file: ${{ inputs.image-definition }} - platforms: ${{ inputs.image-platform }} - push: true - tags: ${{ env.IMAGE_REF }} - cache-from: type=gha - cache-to: type=gha,mode=max - build-args: ${{ inputs.extra-build-arguments }} + # The gha cache backend is reachable from JavaScript actions only, so the build runs without a cache. + env: + IMAGE_DEFINITION: ${{ inputs.image-definition }} + IMAGE_PLATFORM: ${{ inputs.image-platform }} + BUILD_ARGUMENTS: ${{ inputs.extra-build-arguments }} + run: | + args=(--file "$IMAGE_DEFINITION" --platform "$IMAGE_PLATFORM" --tag "$IMAGE_REF" --push) + while IFS= read -r argument; do + [ -n "$argument" ] && args+=(--build-arg "$argument") + done <<< "$BUILD_ARGUMENTS" + docker buildx build "${args[@]}" . - name: Generate SBOM with Syft uses: ./workflow-parts/actions/syft/generate-sbom continue-on-error: true diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index 2f31708..786b27d 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -169,8 +169,7 @@ jobs: sonar-token: ${{ secrets.sonar-token }} - name: Check license compliance with FOSSA if: ${{ inputs.fossa-enabled }} - uses: fossas/fossa-action@ff70fe9fe17cbd2040648f1c45e8ec4e4884dcf3 - id: fossa + uses: ./workflow-parts/actions/fossa/analyze # https://status.fossa.com/ continue-on-error: true timeout-minutes: 3 @@ -178,11 +177,7 @@ jobs: api-key: "${{ secrets.fossa-api-key }}" run-tests: ${{ inputs.fossa-test && github.event_name == 'pull_request' }} test-diff-revision: ${{ github.event.pull_request.base.sha }} - generate-report: html - - name: Create FOSSA report file - if: ${{ inputs.fossa-enabled && steps.fossa.outcome == 'success' }} - run: echo '${{ steps.fossa.outputs.report }}' > report/fossa.html - continue-on-error: true + report-file: report/fossa.html - name: Generate SBOM with Syft uses: ./workflow-parts/actions/syft/generate-sbom continue-on-error: true diff --git a/actions/fossa/analyze/action.yml b/actions/fossa/analyze/action.yml new file mode 100644 index 0000000..0b5cbb9 --- /dev/null +++ b/actions/fossa/analyze/action.yml @@ -0,0 +1,91 @@ +name: Analyze with FOSSA +description: | + Installs the FOSSA CLI by downloading the official binary from its GitHub release and verifying its SHA256 checksum, + then analyzes the dependencies for license compliance, optionally tests them against the policy, and writes an HTML report + (replacement for fossas/fossa-action, to reduce third-party GitHub Action supply-chain risk). + Linux runners only (for example ubuntu-latest). + +inputs: + fossa-version: + description: Version of the FOSSA CLI to install (check latest from https://github.com/fossas/fossa-cli/releases) + required: false + default: "3.19.3" + api-key: + description: FOSSA API key + required: true + run-tests: + description: Test the analyzed dependencies against the FOSSA policy + required: false + default: "false" + test-diff-revision: + description: Revision to report only the issues introduced since (empty tests everything) + required: false + default: "" + report-file: + description: Path of the generated HTML report (empty writes none) + required: false + default: "report/fossa.html" + +runs: + using: "composite" + steps: + - name: Install FOSSA CLI + shell: bash + env: + FOSSA_VERSION: ${{ inputs.fossa-version }} + FOSSA_TARBALL: fossa_${{ inputs.fossa-version }}_linux_amd64.tar.gz + run: | + BASE_URL="https://github.com/fossas/fossa-cli/releases/download/v${FOSSA_VERSION}" + + echo "Downloading FOSSA CLI binary and checksum..." + curl -sSL -O "${BASE_URL}/${FOSSA_TARBALL}" + curl -sSL -O "${BASE_URL}/${FOSSA_TARBALL}.sha256" + + echo "Verifying checksum..." + # The checksum file holds the hash, with or without the file name after it. + echo "$(cut -d ' ' -f 1 "${FOSSA_TARBALL}.sha256") ${FOSSA_TARBALL}" | sha256sum --check --status + + if [ $? -eq 0 ]; then + echo "Verification successful!" + tar -xzf "${FOSSA_TARBALL}" fossa + chmod +x fossa + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv fossa "${RUNNER_TEMP}/bin/fossa" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" + rm -f "${FOSSA_TARBALL}" "${FOSSA_TARBALL}.sha256" + fossa --version + else + echo "ERROR: Checksum verification failed!" >&2 + exit 1 + fi + + - name: Analyze dependencies + shell: bash + env: + FOSSA_API_KEY: ${{ inputs.api-key }} + run: fossa analyze + + - name: Test dependencies against the policy + if: ${{ inputs.run-tests == 'true' }} + shell: bash + env: + FOSSA_API_KEY: ${{ inputs.api-key }} + DIFF_REVISION: ${{ inputs.test-diff-revision }} + run: | + if [ -n "$DIFF_REVISION" ]; then + fossa test --diff "$DIFF_REVISION" + else + fossa test + fi + + - name: Write HTML report + if: ${{ inputs.report-file != '' }} + shell: bash + env: + FOSSA_API_KEY: ${{ inputs.api-key }} + REPORT_FILE: ${{ inputs.report-file }} + run: | + mkdir -p "$(dirname "$REPORT_FILE")" + fossa report attribution --format html > "$REPORT_FILE" From 48f6e55c5e4ea9ab4db60e66bc385a8f8dc36f8a Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 29 Sep 2026 19:45:20 +0200 Subject: [PATCH 4/7] Add a GoReleaser setup action, replacing goreleaser/goreleaser-action Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01McrBgv5WFPJt215Qt8MXLn --- actions/goreleaser/setup/action.yml | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 actions/goreleaser/setup/action.yml diff --git a/actions/goreleaser/setup/action.yml b/actions/goreleaser/setup/action.yml new file mode 100644 index 0000000..985be63 --- /dev/null +++ b/actions/goreleaser/setup/action.yml @@ -0,0 +1,45 @@ +name: Setup GoReleaser +description: | + Installs GoReleaser by downloading the official binary from its GitHub release and verifying its SHA256 checksum + (replacement for goreleaser/goreleaser-action, to reduce third-party GitHub Action supply-chain risk). + Linux runners only (for example ubuntu-latest). + +inputs: + goreleaser-version: + description: Version of GoReleaser to install (check latest from https://github.com/goreleaser/goreleaser/releases) + required: false + default: "2.18.2" + +runs: + using: "composite" + steps: + - name: Install GoReleaser + shell: bash + env: + GORELEASER_VERSION: ${{ inputs.goreleaser-version }} + GORELEASER_TARBALL: goreleaser_Linux_x86_64.tar.gz + run: | + BASE_URL="https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}" + + echo "Downloading GoReleaser binary and checksums..." + curl -sSL -O "${BASE_URL}/${GORELEASER_TARBALL}" + curl -sSL -O "${BASE_URL}/checksums.txt" + + echo "Verifying checksum..." + grep " ${GORELEASER_TARBALL}$" checksums.txt | sha256sum --check --status + + if [ $? -eq 0 ]; then + echo "Verification successful!" + tar -xzf "${GORELEASER_TARBALL}" goreleaser + chmod +x goreleaser + # Installed where the job owns the directory, so neither root nor sudo is needed, on a hosted runner or in a container. + mkdir -p "${RUNNER_TEMP}/bin" + mv goreleaser "${RUNNER_TEMP}/bin/goreleaser" + echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" + export PATH="${RUNNER_TEMP}/bin:$PATH" + rm -f "${GORELEASER_TARBALL}" checksums.txt + goreleaser --version + else + echo "ERROR: Checksum verification failed!" >&2 + exit 1 + fi From 1e879344bc1973327b3e41e5498469fc4957f4d0 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 29 Sep 2026 22:35:09 +0200 Subject: [PATCH 5/7] Add concurrency in ci pipeline --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c814d7e..7e4956e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,10 @@ on: branches: [main] workflow_dispatch: {} +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + permissions: contents: read From 00907950b2c345e8ad6934b07268cef056d50d86 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Tue, 29 Sep 2026 22:38:07 +0200 Subject: [PATCH 6/7] Download each tool into a directory of its own The step ran in the repository root, so a repository holding a terraform directory made unzip fail on it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01McrBgv5WFPJt215Qt8MXLn --- actions/cosign/sign/action.yml | 2 ++ actions/fossa/analyze/action.yml | 2 ++ actions/goreleaser/setup/action.yml | 2 ++ actions/syft/generate-sbom/action.yml | 2 ++ actions/terraform/setup/action.yml | 2 ++ actions/tflint/setup/action.yml | 2 ++ actions/trivy/scan/action.yml | 2 ++ 7 files changed, 14 insertions(+) diff --git a/actions/cosign/sign/action.yml b/actions/cosign/sign/action.yml index a6bb739..f754b0e 100644 --- a/actions/cosign/sign/action.yml +++ b/actions/cosign/sign/action.yml @@ -31,6 +31,8 @@ runs: steps: - name: Install Cosign run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" echo "Downloading Cosign binary and checksums..." curl -sL -O https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/${COSIGN_BINARY} curl -sL -O "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/${COSIGN_CHECKSUM_FILE}" diff --git a/actions/fossa/analyze/action.yml b/actions/fossa/analyze/action.yml index 0b5cbb9..4fa96c2 100644 --- a/actions/fossa/analyze/action.yml +++ b/actions/fossa/analyze/action.yml @@ -35,6 +35,8 @@ runs: FOSSA_VERSION: ${{ inputs.fossa-version }} FOSSA_TARBALL: fossa_${{ inputs.fossa-version }}_linux_amd64.tar.gz run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" BASE_URL="https://github.com/fossas/fossa-cli/releases/download/v${FOSSA_VERSION}" echo "Downloading FOSSA CLI binary and checksum..." diff --git a/actions/goreleaser/setup/action.yml b/actions/goreleaser/setup/action.yml index 985be63..4da2257 100644 --- a/actions/goreleaser/setup/action.yml +++ b/actions/goreleaser/setup/action.yml @@ -19,6 +19,8 @@ runs: GORELEASER_VERSION: ${{ inputs.goreleaser-version }} GORELEASER_TARBALL: goreleaser_Linux_x86_64.tar.gz run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" BASE_URL="https://github.com/goreleaser/goreleaser/releases/download/v${GORELEASER_VERSION}" echo "Downloading GoReleaser binary and checksums..." diff --git a/actions/syft/generate-sbom/action.yml b/actions/syft/generate-sbom/action.yml index c5b05dc..b06e833 100644 --- a/actions/syft/generate-sbom/action.yml +++ b/actions/syft/generate-sbom/action.yml @@ -45,6 +45,8 @@ runs: SYFT_TARBALL: syft_${{ inputs.syft-version }}_linux_amd64.tar.gz SYFT_CHECKSUMS: syft_${{ inputs.syft-version }}_checksums.txt run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" BASE_URL="https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}" echo "Downloading Syft binary and checksums..." diff --git a/actions/terraform/setup/action.yml b/actions/terraform/setup/action.yml index ac7abda..8fd1dab 100644 --- a/actions/terraform/setup/action.yml +++ b/actions/terraform/setup/action.yml @@ -20,6 +20,8 @@ runs: TERRAFORM_ZIP: terraform_${{ inputs.terraform-version }}_linux_amd64.zip TERRAFORM_SHA256SUMS: terraform_${{ inputs.terraform-version }}_SHA256SUMS run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" BASE_URL="https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}" echo "Downloading Terraform binary and checksums..." diff --git a/actions/tflint/setup/action.yml b/actions/tflint/setup/action.yml index ab3ef82..c60b0aa 100644 --- a/actions/tflint/setup/action.yml +++ b/actions/tflint/setup/action.yml @@ -19,6 +19,8 @@ runs: TFLINT_VERSION: ${{ inputs.tflint-version }} TFLINT_ZIP: tflint_linux_amd64.zip run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" BASE_URL="https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}" echo "Downloading TFLint binary and checksums..." diff --git a/actions/trivy/scan/action.yml b/actions/trivy/scan/action.yml index 52c1e26..fd0eb3d 100644 --- a/actions/trivy/scan/action.yml +++ b/actions/trivy/scan/action.yml @@ -52,6 +52,8 @@ runs: TRIVY_TARBALL: trivy_${{ inputs.trivy-version }}_Linux-64bit.tar.gz TRIVY_CHECKSUMS: trivy_${{ inputs.trivy-version }}_checksums.txt run: | + # Downloaded into a directory of its own, since the repository may hold a file or a directory named like the binary. + cd "$(mktemp -d)" BASE_URL="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}" echo "Downloading Trivy binary and checksums..." From 57103f542b0e097d09bb4b851b10c4e06a7d72c7 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Wed, 30 Sep 2026 15:16:43 +0200 Subject: [PATCH 7/7] Add AGENTS.md --- .editorconfig | 4 +++- .markdownlint-cli2.yaml | 3 ++- AGENTS.md | 27 +++++++++++++++++++++++++++ CLAUDE.md | 1 + 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.editorconfig b/.editorconfig index 42b5ed3..f8fec28 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,12 +2,14 @@ root = true [*] end_of_line = lf +insert_final_newline = true indent_size = 2 indent_style = space -insert_final_newline = true [*.{js,json,tf*,yml,yaml}] +max_line_length = 180 trim_trailing_whitespace = true [*.md] +max_line_length = 240 trim_trailing_whitespace = false diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index dae97fc..478087b 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -1,6 +1,7 @@ gitignore: true ignores: - - "**/node_modules/**" + - node_modules + - CLAUDE.md config: # ref. https://github.com/DavidAnson/markdownlint default: true diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..93e9423 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,27 @@ +# github-workflow-parts: agent context + +GitHub workflow actions and reusable workflows. + +Linux or WSL2, Docker and `bash`. + +## Working rules + +- **Every command runs in the foreground, and the agent waits for it.** + No background commands, no subagents, no forks, no parallel tasks, even for a long commands. +- Headers stay: short documentation still has sections. +- Linters for YAML and Markdown are never run by an agent. +- Commit only when asked, and never push. + Shell scripts are `snake_case` and committed with the executable bit (`git update-index --chmod=+x`). +- Documentation is as short as possible. + +## Writing style + +Applies to Markdown, code comments, commit messages and prose in scripts. + +- **A comment says why, not what, and the why is timeless.** +- **One thought per line.** + Every sentence starts on its own line, and there is no maximum line length. +- **No em dash, no en dash.** + A colon, a comma, or a full stop. +- **No second person.** + "The working tree", not "your working tree"; ``, not ``. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md