mirror of
https://github.com/LukeHagar/slinky.git
synced 2025-12-06 12:47:45 +00:00
moving action files, adding publishing, adjusted usage
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build action image
|
- name: Build action image
|
||||||
run: |
|
run: |
|
||||||
docker build -t slinky-action -f .github/actions/slinky/Dockerfile .
|
docker build -t slinky-action -f Dockerfile .
|
||||||
|
|
||||||
- name: Run action container (expect nonzero if failures)
|
- name: Run action container (expect nonzero if failures)
|
||||||
id: run_action
|
id: run_action
|
||||||
|
|||||||
2
.github/workflows/example-slinky.yml
vendored
2
.github/workflows/example-slinky.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run Slinky link checker
|
- name: Run Slinky link checker
|
||||||
uses: ./\.github/actions/slinky
|
uses: ./
|
||||||
with:
|
with:
|
||||||
path: .
|
path: .
|
||||||
patterns: "**/*"
|
patterns: "**/*"
|
||||||
|
|||||||
74
.github/workflows/publish.yml
vendored
Normal file
74
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
name: Publish Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Compute next v1 tag
|
||||||
|
id: compute
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
const tags = await github.paginate(github.rest.repos.listTags, { owner, repo, per_page: 100 });
|
||||||
|
const v1tags = tags
|
||||||
|
.map(t => t.name)
|
||||||
|
.filter(name => /^v1\.\d+\.\d+$/.test(name));
|
||||||
|
let nextPatch = 0;
|
||||||
|
let minor = 0;
|
||||||
|
if (v1tags.length > 0) {
|
||||||
|
let maxPatch = 0;
|
||||||
|
for (const name of v1tags) {
|
||||||
|
const [, mi, pa] = name.match(/^v1\.(\d+)\.(\d+)$/);
|
||||||
|
const m = parseInt(mi, 10);
|
||||||
|
const p = parseInt(pa, 10);
|
||||||
|
if (m > minor) { minor = m; maxPatch = p; }
|
||||||
|
else if (m === minor && p > maxPatch) { maxPatch = p; }
|
||||||
|
}
|
||||||
|
nextPatch = maxPatch + 1;
|
||||||
|
}
|
||||||
|
const newTag = `v1.${minor}.${nextPatch}`;
|
||||||
|
core.setOutput('tag', newTag);
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ steps.compute.outputs.tag }}
|
||||||
|
release_name: ${{ steps.compute.outputs.tag }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Update floating v1 tag to latest
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
const sha = context.sha;
|
||||||
|
const ref = 'tags/v1';
|
||||||
|
try {
|
||||||
|
await github.rest.git.getRef({ owner, repo, ref });
|
||||||
|
await github.rest.git.updateRef({ owner, repo, ref, sha, force: true });
|
||||||
|
} catch (e) {
|
||||||
|
await github.rest.git.createRef({ owner, repo, ref: `refs/${ref}`, sha });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
FROM golang:1.24 as build
|
||||||
|
WORKDIR /app
|
||||||
|
# Expect the repository root as build context when building this image
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 go build -o /usr/local/bin/slinky ./
|
||||||
|
|
||||||
|
FROM alpine:3.20
|
||||||
|
RUN adduser -D -u 10001 appuser \
|
||||||
|
&& apk add --no-cache curl jq ca-certificates
|
||||||
|
COPY --from=build /usr/local/bin/slinky /usr/local/bin/slinky
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
USER appuser
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
|
||||||
|
|
||||||
2
Makefile
2
Makefile
@@ -21,7 +21,7 @@ check: build
|
|||||||
|
|
||||||
# Build the Docker-based GitHub Action locally
|
# Build the Docker-based GitHub Action locally
|
||||||
action-image:
|
action-image:
|
||||||
docker build -t slinky-action -f .github/actions/slinky/Dockerfile .
|
docker build -t slinky-action -f Dockerfile .
|
||||||
|
|
||||||
# Run the Action container against the current repo
|
# Run the Action container against the current repo
|
||||||
action-run: action-image
|
action-run: action-image
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Run Slinky
|
- name: Run Slinky
|
||||||
uses: ./.github/actions/slinky
|
uses: owner/repo@v1
|
||||||
with:
|
with:
|
||||||
path: .
|
path: .
|
||||||
patterns: "**/*"
|
patterns: "**/*"
|
||||||
|
|||||||
72
action.yml
Normal file
72
action.yml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
name: "Slinky Link Checker"
|
||||||
|
description: "Slink through your repository looking for dead links"
|
||||||
|
author: "LukeHagar"
|
||||||
|
branding:
|
||||||
|
icon: "link"
|
||||||
|
color: "blue"
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
path:
|
||||||
|
description: "Root path to scan"
|
||||||
|
required: false
|
||||||
|
default: "."
|
||||||
|
patterns:
|
||||||
|
description: "Comma-separated doublestar patterns. Ex: docs/**/*.md,**/*.go; default **/*"
|
||||||
|
required: false
|
||||||
|
default: "**/*"
|
||||||
|
concurrency:
|
||||||
|
description: "Maximum concurrent requests"
|
||||||
|
required: false
|
||||||
|
default: "16"
|
||||||
|
timeout:
|
||||||
|
description: "HTTP timeout seconds"
|
||||||
|
required: false
|
||||||
|
default: "10"
|
||||||
|
json-out:
|
||||||
|
description: "Optional path to write JSON results"
|
||||||
|
required: false
|
||||||
|
default: "results.json"
|
||||||
|
md-out:
|
||||||
|
description: "Optional path to write Markdown report for PR comment"
|
||||||
|
required: false
|
||||||
|
default: "results.md"
|
||||||
|
repo-blob-base:
|
||||||
|
description: "Override GitHub blob base URL (https://github.com/<owner>/<repo>/blob/<sha>)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
fail-on-failures:
|
||||||
|
description: "Fail the job if any links fail"
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
comment-pr:
|
||||||
|
description: "If running on a PR, post a comment with the report"
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
step-summary:
|
||||||
|
description: "Append the report to the GitHub Step Summary"
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "docker"
|
||||||
|
image: "Dockerfile"
|
||||||
|
args: []
|
||||||
|
env:
|
||||||
|
INPUT_PATH: ${{ inputs.path }}
|
||||||
|
INPUT_PATTERNS: ${{ inputs.patterns }}
|
||||||
|
INPUT_CONCURRENCY: ${{ inputs.concurrency }}
|
||||||
|
INPUT_TIMEOUT: ${{ inputs.timeout }}
|
||||||
|
INPUT_JSON_OUT: ${{ inputs["json-out"] }}
|
||||||
|
INPUT_MD_OUT: ${{ inputs["md-out"] }}
|
||||||
|
INPUT_REPO_BLOB_BASE: ${{ inputs["repo-blob-base"] }}
|
||||||
|
INPUT_FAIL_ON_FAILURES: ${{ inputs["fail-on-failures"] }}
|
||||||
|
INPUT_COMMENT_PR: ${{ inputs["comment-pr"] }}
|
||||||
|
INPUT_STEP_SUMMARY: ${{ inputs["step-summary"] }}
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
json-path:
|
||||||
|
description: "Path to JSON results file"
|
||||||
|
md-path:
|
||||||
|
description: "Path to Markdown report file"
|
||||||
|
|
||||||
|
|
||||||
89
entrypoint.sh
Normal file
89
entrypoint.sh
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
PATH_ARG="${INPUT_PATH:-.}"
|
||||||
|
PATTERNS_ARG="${INPUT_PATTERNS:-**/*}"
|
||||||
|
CONCURRENCY_ARG="${INPUT_CONCURRENCY:-16}"
|
||||||
|
TIMEOUT_ARG="${INPUT_TIMEOUT:-10}"
|
||||||
|
JSON_OUT_ARG="${INPUT_JSON_OUT:-results.json}"
|
||||||
|
MD_OUT_ARG="${INPUT_MD_OUT:-results.md}"
|
||||||
|
REPO_BLOB_BASE_ARG="${INPUT_REPO_BLOB_BASE:-}"
|
||||||
|
FAIL_ON_FAILURES_ARG="${INPUT_FAIL_ON_FAILURES:-true}"
|
||||||
|
COMMENT_PR_ARG="${INPUT_COMMENT_PR:-true}"
|
||||||
|
STEP_SUMMARY_ARG="${INPUT_STEP_SUMMARY:-true}"
|
||||||
|
|
||||||
|
ARGS="check \"${PATH_ARG}\" --concurrency ${CONCURRENCY_ARG} --timeout ${TIMEOUT_ARG}"
|
||||||
|
if [ "${FAIL_ON_FAILURES_ARG}" = "true" ]; then
|
||||||
|
ARGS="$ARGS --fail-on-failures true"
|
||||||
|
else
|
||||||
|
ARGS="$ARGS --fail-on-failures false"
|
||||||
|
fi
|
||||||
|
if [ -n "${PATTERNS_ARG}" ]; then
|
||||||
|
NORM_PATTERNS=$(printf "%s" "${PATTERNS_ARG}" | sed 's/,\s*/,/g')
|
||||||
|
IFS=','
|
||||||
|
set -- $NORM_PATTERNS
|
||||||
|
unset IFS
|
||||||
|
for pat in "$@"; do
|
||||||
|
ARGS="$ARGS --patterns \"$pat\""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [ -n "${JSON_OUT_ARG}" ]; then
|
||||||
|
ARGS="$ARGS --json-out \"${JSON_OUT_ARG}\""
|
||||||
|
fi
|
||||||
|
if [ -n "${MD_OUT_ARG}" ]; then
|
||||||
|
ARGS="$ARGS --md-out \"${MD_OUT_ARG}\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compute GitHub blob base URL for file links used in the Markdown report
|
||||||
|
if [ -n "${REPO_BLOB_BASE_ARG}" ]; then
|
||||||
|
export SLINKY_REPO_BLOB_BASE_URL="${REPO_BLOB_BASE_ARG}"
|
||||||
|
elif [ -n "${GITHUB_REPOSITORY:-}" ]; then
|
||||||
|
COMMIT_SHA="${GITHUB_SHA:-}"
|
||||||
|
if [ -n "${GITHUB_EVENT_PATH:-}" ] && command -v jq >/dev/null 2>&1; then
|
||||||
|
PR_HEAD_SHA="$(jq -r '.pull_request.head.sha // empty' "$GITHUB_EVENT_PATH" || true)"
|
||||||
|
if [ -n "$PR_HEAD_SHA" ]; then
|
||||||
|
COMMIT_SHA="$PR_HEAD_SHA"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -n "$COMMIT_SHA" ]; then
|
||||||
|
export SLINKY_REPO_BLOB_BASE_URL="https://github.com/${GITHUB_REPOSITORY}/blob/${COMMIT_SHA}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval slinky ${ARGS}
|
||||||
|
|
||||||
|
# Expose outputs
|
||||||
|
if [ -n "${GITHUB_OUTPUT:-}" ]; then
|
||||||
|
if [ -n "${JSON_OUT_ARG}" ]; then
|
||||||
|
echo "json-path=${JSON_OUT_ARG}" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
if [ -n "${MD_OUT_ARG}" ]; then
|
||||||
|
echo "md-path=${MD_OUT_ARG}" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Append report to job summary if requested
|
||||||
|
if [ "${STEP_SUMMARY_ARG}" = "true" ] && [ -n "${GITHUB_STEP_SUMMARY:-}" ] && [ -n "${MD_OUT_ARG}" ] && [ -f "${MD_OUT_ARG}" ]; then
|
||||||
|
cat "${MD_OUT_ARG}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Post PR comment if this is a PR and requested
|
||||||
|
if [ "${COMMENT_PR_ARG}" = "true" ] && [ -n "${MD_OUT_ARG}" ] && [ -f "${MD_OUT_ARG}" ]; then
|
||||||
|
PR_NUMBER=""
|
||||||
|
if [ -n "${GITHUB_EVENT_PATH:-}" ] && command -v jq >/dev/null 2>&1; then
|
||||||
|
PR_NUMBER="$(jq -r '.pull_request.number // empty' "$GITHUB_EVENT_PATH" || true)"
|
||||||
|
fi
|
||||||
|
if [ -n "${PR_NUMBER}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_TOKEN:-}" ]; then
|
||||||
|
BODY_CONTENT="$(cat "${MD_OUT_ARG}")"
|
||||||
|
curl -sS -H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||||
|
-X POST "https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
|
||||||
|
-d "$(printf '{"body": %s}' "$(jq -Rs . <<EOF
|
||||||
|
${BODY_CONTENT}
|
||||||
|
EOF
|
||||||
|
)" )" >/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user