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
43 changes: 43 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
on:
push:
branches:
- feat/add-action

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
actions: read
outputs:
changed: ${{ steps.changes.outputs.changed }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- uses: "./"
id: changes
with:
workflow-file: "main.yml"
include: .github/workflows/
exclude: |
.github/workflows/test2.yml
README.md
somefile

- run: |
echo "Changed: ${{ steps.changes.outputs.changes_detected }}"
echo "Base SHA: ${{ steps.changes.outputs.base_sha }}"
echo "Head SHA: ${{ steps.changes.outputs.head_sha }}"
echo "Changed files: ${{ steps.changes.outputs.changed_files }}"

deploy:
needs: check
if: needs.check.outputs.changes_detected == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- run: echo "insert deploy here"
42 changes: 42 additions & 0 deletions .github/workflows/test2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
on:
push:
branches:
- feat/never-ran-this-before

jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
actions: read
outputs:
changed: ${{ steps.changes.outputs.changed }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- uses: "Arbeidstilsynet/action-check-changes@feat/add-action"
id: changes
with:
include: |
.
exclude: |
.github/workflows/
README.md

- run: |
echo "Changed: ${{ steps.changes.outputs.changed_files }}"
echo "Base SHA: ${{ steps.changes.outputs.base_sha }}"
echo "Head SHA: ${{ steps.changes.outputs.head_sha }}"
echo "Changed files: ${{ steps.changes.outputs.changed_files }}"

deploy:
needs: check
if: needs.check.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- run: echo "insert deploy here"
68 changes: 41 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Arbeidstilsynet/action-noop
# Arbeidstilsynet/action-check-changes

> **Note:** This is a template repository for creating simple composite GitHub Actions.
>
> After creating the new repo you should enable "Allow auto-merge" and "Automatically delete head branches" in Settings -> General -> Pull Requests.
Action to check for changes to files since last successful workflow run.

A no-op GitHub Action that echoes inputs and sets outputs.
This action can be used for safe continuous delivery/deployment taking into consideration previous runs.

## Versioning

Expand All @@ -14,46 +12,62 @@ If you have to make breaking changes to the action, bump the version.

## Requirements

- None
Requires full commit history. When configuring [actions/checkout](https://github.com/actions/checkout), make sure to set `fetch-depth: 0`.

The job must have permission for `actions: read` for the action to retrieve run history through the GitHub API.

## Inputs

| Name | Description | Required | Default |
|--------------|--------------------|----------|-----------------|
| `input-one` | First input value | Yes | |
| `input-two` | Second input value | No | `default-value` |
| Name | Required | Default | Description |
|-----------------|----------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `include` | Yes | | Newline separated Git pathspecs (globs) to include in the diff scope (e.g. `src/`, `app/**/*.ts`). |
| `exclude` | No | (empty) | Newline separated Git pathspecs to exclude. Each is applied as `:(exclude)<pattern>`. |
| `workflow-file` | No | (auto-detect) | Workflow filename whose last successful run determines the base commit. If omitted, auto-detected from `GITHUB_WORKFLOW_REF`; if none found falls back to repo root commit. |

## Outputs

| Name | Description |
|-------------|-----------------------|
| `output-one`| Echo of input-one |
| `output-two`| Echo of input-two |
| Name | Description |
|--------------------|-------------------------------------------------------------------------------------------------------|
| `changes_detected` | `true` if any included (and not excluded) paths changed between base and head. |
| `base_sha` | The base commit SHA used for the diff (last successful run’s head, or fallback). |
| `head_sha` | The current commit SHA. |
| `changed_files` | Newline separated list of changed files after exclusions. URL-escaped newlines in raw output context. |

## Usage

```yaml
name: Example No-op Action Usage

on:
push:
branches:
- main

jobs:
noop-job:
check:
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
outputs:
changed: ${{ steps.changes.outputs.changes_detected }}
steps:
- uses: actions/checkout@v4

- uses: Arbeidstilsynet/action-noop@v1
id: noop
- uses: actions/checkout@v5
with:
input-one: "Hello"
input-two: "World"
fetch-depth: 0

- name: Show outputs
run: |
echo "Output one: ${{ steps.noop.outputs.output-one }}"
echo "Output two: ${{ steps.noop.outputs.output-two }}"
- uses: Arbeidstilsynet/action-check-changes@v1
id: changes
with:
include: |
apps/web
.github/workflows/deploy.yml
exclude: |
**/README.md
apps/web/docs/

deploy:
needs: check
if: needs.check.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
- run: echo "insert deploy here"
```
168 changes: 148 additions & 20 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,160 @@
name: "No-op Action"
name: "Check changes"
author: "Arbeidstilsynet"
description: "A no-op GitHub Action that echoes inputs and sets outputs."
description: "Check if specified paths changed since last successful run of a workflow"

inputs:
input-one:
description: "First input value"
include:
description: Newline separated list of Git pathspecs to include
required: true
input-two:
description: "Second input value"
exclude:
description: Newline separated list of Git pathspecs to exclude
required: false
default: ""
workflow-file:
description: Workflow file (e.g. deploy.yml) whose last successful run determines the base commit
required: false
default: "default-value"

outputs:
output-one:
description: "Echo of input-one"
value: ${{ steps.noop.outputs.output-one }}
output-two:
description: "Echo of input-two"
value: ${{ steps.noop.outputs.output-two }}
changes_detected:
description: "true if any included (and not excluded) paths changed"
value: ${{ steps.diff.outputs.changed }}
base_sha:
description: Base commit used for diff
value: ${{ steps.base.outputs.base_sha }}
head_sha:
description: Current HEAD sha
value: ${{ steps.base.outputs.head_sha }}
changed_files:
description: Newline separated list of changed files after exclusions
value: ${{ steps.diff.outputs.changed_files }}

runs:
using: "composite"
using: composite
steps:
- name: Echo inputs and set outputs
id: noop
- name: Determine base (last successful run)
id: base
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

# Resolve workflow file (input or auto-detect)
if [ -n "${{ inputs.workflow-file }}" ]; then
WF_FILE='${{ inputs.workflow-file }}'
else
WF_FILE=$(echo "${GITHUB_WORKFLOW_REF:-}" | sed -n 's#.*/\.github/workflows/\([^@]*\)@.*#\1#p')
fi

if [ -n "$WF_FILE" ]; then
echo "Workflow file: $WF_FILE"
else
echo "No workflow file name available (cannot query runs)."
fi

BRANCH='${{ github.ref_name }}'
HEAD_SHA=$(git rev-parse HEAD)
BASE_SHA=""
LAST_SUCCESS=""

if [ -n "$WF_FILE" ] && command -v gh >/dev/null 2>&1; then
echo "Fetching workflow runs (branch=$BRANCH)..."
set +e # handle gh api failures gracefully
API_OUTPUT=$(gh api "repos/${{ github.repository }}/actions/workflows/${WF_FILE}2/runs?branch=${BRANCH}" 2>&1)
RC=$?
echo "RC=$RC"
set -e

if [ $RC -ne 0 ]; then
if echo "$API_OUTPUT" | grep -q '"status": *"404"' || echo "$API_OUTPUT" | grep -qi 'Not Found'; then
echo "Workflow not found (404) - treating as no prior runs."
API_OUTPUT=""
else
echo "gh api failed (exit $RC). Output:"
echo "$API_OUTPUT"
exit 1
fi
fi

if [ -z "$API_OUTPUT" ]; then
echo "No workflow runs data."
else
RUNS=$(echo "$API_OUTPUT" | jq -r '(.workflow_runs? // []) | length' 2>/dev/null || echo 0)
echo "Detected $RUNS run(s)."
LAST_SUCCESS=$(echo "$API_OUTPUT" | jq -r '(.workflow_runs? // []) | map(select(.conclusion=="success")) | .[0].head_sha // empty' 2>/dev/null || echo "")
echo "Last successful commit SHA: ${LAST_SUCCESS:-none}"
fi
else
echo "Skipping API lookup (missing workflow file or gh CLI)."
fi

if [ -z "$LAST_SUCCESS" ]; then
echo "No previous successful run; using first commit as base."
BASE_SHA=$(git rev-list --max-parents=0 HEAD)
else
BASE_SHA="$LAST_SUCCESS"
fi

echo "Base SHA: $BASE_SHA"
echo "Head SHA: $HEAD_SHA"

{
echo "base_sha=$BASE_SHA"
echo "head_sha=$HEAD_SHA"
} >> "$GITHUB_OUTPUT"

- name: Compute diff
id: diff
shell: bash
run: |
echo "input-one: ${{ inputs.input-one }}"
echo "input-two: ${{ inputs.input-two }}"
echo "output-one=${{ inputs.input-one }}" >> $GITHUB_OUTPUT
echo "output-two=${{ inputs.input-two }}" >> $GITHUB_OUTPUT
set -euo pipefail
BASE_SHA='${{ steps.base.outputs.base_sha }}'
HEAD_SHA='${{ steps.base.outputs.head_sha }}'
INCLUDE_RAW='${{ inputs.include }}'
EXCLUDE_RAW='${{ inputs.exclude }}'

if [ -z "$INCLUDE_RAW" ]; then
echo "No include paths provided" >&2
exit 1
fi

# New: only split on newlines so patterns may contain spaces.
norm_list () {
# Read stdin, trim leading/trailing whitespace, drop empty lines, dedupe.
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e '/^$/d' | sort -u
}

mapfile -t INCLUDES < <(printf "%s\n" "$INCLUDE_RAW" | norm_list)
mapfile -t EXCLUDES < <(printf "%s\n" "$EXCLUDE_RAW" | norm_list)

echo "Include patterns:"
printf ' %s\n' "${INCLUDES[@]}"
if [ ${#EXCLUDES[@]} -gt 0 ]; then
echo "Exclude patterns:"
printf ' %s\n' "${EXCLUDES[@]}"
else
echo "No exclude patterns."
fi

EXCLUDE_SPEC=()
for p in "${EXCLUDES[@]}"; do
EXCLUDE_SPEC+=(":(exclude)$p")
done

git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- "${INCLUDES[@]}" "${EXCLUDE_SPEC[@]}" > /tmp/changed_files || true

if [ -s /tmp/changed_files ]; then
CHANGED=true
else
CHANGED=false
fi

echo "Changed: $CHANGED"
cat /tmp/changed_files || true

{
echo "changed=$CHANGED"
printf "changed_files="
awk '{printf "%s%s", NR==1?"":"%0A", $0}' /tmp/changed_files
echo
} >> "$GITHUB_OUTPUT"