mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-10-07 15:08:56 +00:00
fix(ci): load the isolation checker from this workflow's commit
The second checkout used the caller's SHA and the caller's token, so a private clone of this repo could not resolve the script. The checker is now a composite action referenced with $/.
This commit is contained in:
parent
e7712e6d0f
commit
39a6a91c9c
5 changed files with 97 additions and 50 deletions
6
.github/actionlint.yaml
vendored
Normal file
6
.github/actionlint.yaml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# actionlint 1.7.12 rejects `$/`, which GitHub accepts as a self-repository
|
||||
# action reference (runner 2.336.0+). Drop this ignore when a release parses it.
|
||||
paths:
|
||||
.github/workflows/ci-terraform.yaml:
|
||||
ignore:
|
||||
- 'specifying action "\$/.github/actions/app-terraform-isolation" in invalid format because ref is missing'
|
||||
64
.github/actions/app-terraform-isolation/action.yml
vendored
Normal file
64
.github/actions/app-terraform-isolation/action.yml
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
name: App and Terraform isolation
|
||||
description: Fail when a change set mixes Terraform with deployable application files.
|
||||
|
||||
inputs:
|
||||
app-paths:
|
||||
description: Newline-separated deployable paths. A trailing slash is a prefix. Any other entry is an exact file.
|
||||
required: true
|
||||
terraform-dir:
|
||||
description: Directory containing Terraform sources.
|
||||
required: true
|
||||
default: terraform
|
||||
event-name:
|
||||
description: github.event_name from the calling workflow.
|
||||
required: true
|
||||
pr-base-sha:
|
||||
description: pull_request base SHA. Empty outside pull_request.
|
||||
required: false
|
||||
default: ""
|
||||
merge-group-base-sha:
|
||||
description: merge_group base SHA. Empty outside merge_group.
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Classify changed paths
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
APP_PATHS: ${{ inputs.app-paths }}
|
||||
TERRAFORM_DIR: ${{ inputs.terraform-dir }}
|
||||
EVENT_NAME: ${{ inputs.event-name }}
|
||||
PR_BASE_SHA: ${{ inputs.pr-base-sha }}
|
||||
MERGE_GROUP_BASE_SHA: ${{ inputs.merge-group-base-sha }}
|
||||
CHECKER: ${{ github.action_path }}/check_app_terraform_isolation.py
|
||||
run: |
|
||||
set -euo pipefail
|
||||
classify() {
|
||||
python3 "${CHECKER}"
|
||||
}
|
||||
case "${EVENT_NAME}" in
|
||||
pull_request)
|
||||
if [[ -z "${PR_BASE_SHA}" ]]; then
|
||||
echo "FAIL: pull_request base SHA is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
merge_base="$(git merge-base "${PR_BASE_SHA}" HEAD)"
|
||||
git diff --name-only --diff-filter=ACMRD "${merge_base}" HEAD | classify
|
||||
;;
|
||||
merge_group)
|
||||
if [[ -z "${MERGE_GROUP_BASE_SHA}" ]]; then
|
||||
echo "FAIL: merge_group base SHA is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
while IFS= read -r sha; do
|
||||
[[ -z "${sha}" ]] && continue
|
||||
git diff --name-only --diff-filter=ACMRD "${sha}^" "${sha}" | classify
|
||||
done < <(git rev-list --reverse --first-parent "${MERGE_GROUP_BASE_SHA}..HEAD")
|
||||
;;
|
||||
*)
|
||||
echo "SKIP: live isolation runs on pull_request and merge_group (event: ${EVENT_NAME})"
|
||||
;;
|
||||
esac
|
||||
55
.github/workflows/ci-terraform.yaml
vendored
55
.github/workflows/ci-terraform.yaml
vendored
|
|
@ -21,6 +21,9 @@ name: CI — Terraform
|
|||
# merge_group classifies each first-parent commit against its parent, so a
|
||||
# Terraform-only PR stacked with an app-only PR still passes. The classified
|
||||
# diff includes deletions. Terraform paths are those under working-directory.
|
||||
# The checker is a composite action in this repository. `$/` resolves that
|
||||
# action at this workflow's commit, so callers do not clone this private
|
||||
# repository with their GITHUB_TOKEN.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
|
@ -72,50 +75,12 @@ jobs:
|
|||
- name: Terraform validate
|
||||
run: terraform validate
|
||||
|
||||
- name: Checkout isolation checker
|
||||
if: inputs.app-paths != ''
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
repository: Sea-Haven-Industries/.github
|
||||
ref: ${{ github.workflow_sha }}
|
||||
path: .ci-org-github
|
||||
persist-credentials: false
|
||||
|
||||
- name: App and Terraform isolation
|
||||
if: inputs.app-paths != ''
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
APP_PATHS: ${{ inputs.app-paths }}
|
||||
TERRAFORM_DIR: ${{ inputs.working-directory }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }}
|
||||
CHECKER: ${{ github.workspace }}/.ci-org-github/scripts/check_app_terraform_isolation.py
|
||||
run: |
|
||||
set -euo pipefail
|
||||
classify() {
|
||||
python3 "${CHECKER}"
|
||||
}
|
||||
case "${EVENT_NAME}" in
|
||||
pull_request)
|
||||
if [[ -z "${PR_BASE_SHA}" ]]; then
|
||||
echo "FAIL: pull_request base SHA is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
merge_base="$(git merge-base "${PR_BASE_SHA}" HEAD)"
|
||||
git diff --name-only --diff-filter=ACMRD "${merge_base}" HEAD | classify
|
||||
;;
|
||||
merge_group)
|
||||
if [[ -z "${MERGE_GROUP_BASE_SHA}" ]]; then
|
||||
echo "FAIL: merge_group base SHA is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
while IFS= read -r sha; do
|
||||
[[ -z "${sha}" ]] && continue
|
||||
git diff --name-only --diff-filter=ACMRD "${sha}^" "${sha}" | classify
|
||||
done < <(git rev-list --reverse --first-parent "${MERGE_GROUP_BASE_SHA}..HEAD")
|
||||
;;
|
||||
*)
|
||||
echo "SKIP: live isolation runs on pull_request and merge_group (event: ${EVENT_NAME})"
|
||||
;;
|
||||
esac
|
||||
uses: $/.github/actions/app-terraform-isolation
|
||||
with:
|
||||
app-paths: ${{ inputs.app-paths }}
|
||||
terraform-dir: ${{ inputs.working-directory }}
|
||||
event-name: ${{ github.event_name }}
|
||||
pr-base-sha: ${{ github.event.pull_request.base.sha }}
|
||||
merge-group-base-sha: ${{ github.event.merge_group.base_sha }}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,19 @@ from __future__ import annotations
|
|||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from check_app_terraform_isolation import first_isolation_violation, isolation_violation
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ACTION = ROOT / ".github" / "actions" / "app-terraform-isolation"
|
||||
WORKFLOW = ROOT / ".github" / "workflows" / "ci-terraform.yaml"
|
||||
sys.path.insert(0, str(ACTION))
|
||||
|
||||
WORKFLOW = (
|
||||
Path(__file__).resolve().parents[1] / ".github" / "workflows" / "ci-terraform.yaml"
|
||||
from check_app_terraform_isolation import ( # noqa: E402
|
||||
first_isolation_violation,
|
||||
isolation_violation,
|
||||
)
|
||||
|
||||
APP_PATHS = "src/\npackage.json\npackage-lock.json\n"
|
||||
|
|
@ -133,10 +138,16 @@ class WorkflowDiffTests(unittest.TestCase):
|
|||
|
||||
def test_workflow_passes_working_directory(self) -> None:
|
||||
self.assertIn(
|
||||
"TERRAFORM_DIR: ${{ inputs.working-directory }}",
|
||||
"terraform-dir: ${{ inputs.working-directory }}",
|
||||
WORKFLOW.read_text(),
|
||||
)
|
||||
|
||||
def test_checker_is_this_repos_action_at_the_workflow_commit(self) -> None:
|
||||
text = WORKFLOW.read_text()
|
||||
self.assertIn("uses: $/.github/actions/app-terraform-isolation", text)
|
||||
self.assertNotIn("github.workflow_sha", text)
|
||||
self.assertNotIn("repository: Sea-Haven-Industries/.github", text)
|
||||
|
||||
def test_deleted_app_file_is_classified(self) -> None:
|
||||
diff_filter = _workflow_diff_filters()[0]
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
|
|
@ -173,7 +184,8 @@ class WorkflowDiffTests(unittest.TestCase):
|
|||
|
||||
|
||||
def _workflow_diff_filters() -> list[str]:
|
||||
return re.findall(r"--diff-filter=([A-Z]+)", WORKFLOW.read_text())
|
||||
action = (ACTION / "action.yml").read_text()
|
||||
return re.findall(r"--diff-filter=([A-Z]+)", action)
|
||||
|
||||
|
||||
def _git(repo: Path, *args: str) -> str:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue