ci: build on GitHub via the mirror, switchable with BUILD_ON #5
No files matched your search
+44
-1
@@ -1,6 +1,14 @@
|
||||
# CI on the self-hosted Gitea, ported from .gitlab-ci.yml during the move off
|
||||
# GitLab (2026-09-22). Gitea reads .gitea/workflows and ignores .github/ once
|
||||
# this directory exists; .github/workflows stays as it was for GitHub.
|
||||
# this directory exists.
|
||||
#
|
||||
# BUILD_ON: an org variable, set on both forges. Gitea push-mirrors this
|
||||
# repository to GitHub, and with BUILD_ON=github the shellcheck job here skips
|
||||
# and GitHub Actions runs the same lint on the mirrored copy
|
||||
# (.github/workflows/ci.yml). The `github` job at the end waits for the result
|
||||
# that workflow reports back, so this run still passes or fails with the lint.
|
||||
# Unset, everything runs here as before -- which is also the fallback if
|
||||
# GitHub is ever unavailable.
|
||||
#
|
||||
# Every job runs in an image pinned by digest (tag in the trailing comment),
|
||||
# and the only action used is coffey-labs/actions/checkout pinned by SHA. The
|
||||
@@ -19,6 +27,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
if: ${{ vars.BUILD_ON != 'github' }}
|
||||
runs-on: light
|
||||
# The -alpine variant, not koalaman/shellcheck:stable. That one is built
|
||||
# FROM scratch with shellcheck as the entrypoint and no shell at all, so a
|
||||
@@ -34,3 +43,37 @@ jobs:
|
||||
[ -n "$files" ] || { echo "no shell scripts found"; exit 1; }
|
||||
echo "$files" | tr '\n' ' '
|
||||
shellcheck --severity=warning --format=gcc $files
|
||||
|
||||
# ------------------------------------------------------------ github ------
|
||||
# With BUILD_ON=github the jobs above skip and GitHub Actions builds this
|
||||
# commit on the push-mirrored copy (.github/workflows/ci.yml). This job waits
|
||||
# for the commit status that workflow reports back, and passes or fails with
|
||||
# it, so this run -- what pull requests and merges look at -- still carries
|
||||
# the real result. If GitHub never reports, it times out and fails: the cue
|
||||
# to unset BUILD_ON and build here again.
|
||||
github:
|
||||
if: ${{ vars.BUILD_ON == 'github' }}
|
||||
runs-on: light
|
||||
timeout-minutes: 150
|
||||
container:
|
||||
image: node:26-bookworm-slim@sha256:582460f614631b59b824ac6020533b9bf339c7fdf3a6d7db31abb6b4065f0212 # 26-bookworm-slim
|
||||
env:
|
||||
READ_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
CONTEXT: ${{ github.ref_type == 'tag' && 'github/ci (tag)' || 'github/ci (branch)' }}
|
||||
steps:
|
||||
- run: apt-get update -qq && apt-get install -y -qq --no-install-recommends ca-certificates curl jq >/dev/null
|
||||
- shell: bash
|
||||
run: |
|
||||
set -uo pipefail
|
||||
url="${CI_SERVER_INTERNAL}/api/v1/repos/${GITHUB_REPOSITORY}/commits/${SHA}/statuses?limit=50"
|
||||
echo "Waiting for '${CONTEXT}' on ${SHA}"
|
||||
while :; do
|
||||
# The newest status for the context is the one that counts.
|
||||
s="$(curl -fsS -H "Authorization: token ${READ_TOKEN}" "$url" | jq -r --arg c "$CONTEXT" '[.[] | select(.context == $c)] | sort_by(.id) | last | "\(.status) \(.target_url)"' 2>/dev/null)"
|
||||
case "$s" in
|
||||
success\ *) echo "GitHub: ${s}"; exit 0 ;;
|
||||
failure\ *|error\ *) echo "GitHub: ${s}" >&2; exit 1 ;;
|
||||
esac
|
||||
sleep 20
|
||||
done
|
||||
@@ -0,0 +1,88 @@
|
||||
# ShellCheck on GitHub Actions, for the GitHub replica of this repository.
|
||||
# Gitea (git.coffeylabs.org) is the source of truth: it pushes every commit
|
||||
# here through a push mirror, and this workflow lints what arrives. Pull
|
||||
# requests live on Gitea, so there is no pull_request trigger -- a PR's branch
|
||||
# reaches this copy as an ordinary push.
|
||||
#
|
||||
# Every file in this repository is a shell script that people are invited to
|
||||
# run as root, so a lint gate is the cheapest guard against the class of bug
|
||||
# that keeps showing up here: unquoted expansions, values interpolated into
|
||||
# commands, and exit statuses that go unchecked.
|
||||
#
|
||||
# The switch is the org variable BUILD_ON, set on both forges. With
|
||||
# BUILD_ON=github every job here runs, the job in .gitea/workflows/ci.yml
|
||||
# skips, and Gitea's own run waits for the result this workflow reports back.
|
||||
# With it unset, every job here skips and Gitea lints as before -- which is
|
||||
# also the way back if GitHub is ever unavailable: unset it on Gitea.
|
||||
#
|
||||
# The job is .gitea/workflows/ci.yml's, in the same image pinned by digest,
|
||||
# with the same settings (severity=warning, gcc format, every script in one
|
||||
# run). The only action used is actions/checkout, pinned to a full commit SHA
|
||||
# with the release in a trailing comment.
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['**']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL }}
|
||||
|
||||
jobs:
|
||||
# Tells Gitea a result is on its way, so the commit does not look unchecked
|
||||
# while the checks run.
|
||||
pending:
|
||||
if: ${{ vars.BUILD_ON == 'github' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
CONTEXT: github/ci (${{ github.ref_type }})
|
||||
run: |
|
||||
jq -n --arg c "$CONTEXT" --arg u "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
||||
'{state:"pending", context:$c, target_url:$u, description:"GitHub Actions"}' |
|
||||
curl -fsS -o /dev/null -X POST -H "Authorization: token $GITEA_TOKEN" -H 'Content-Type: application/json' \
|
||||
--data @- "$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA"
|
||||
|
||||
shellcheck:
|
||||
if: ${{ vars.BUILD_ON == 'github' }}
|
||||
runs-on: ubuntu-latest
|
||||
# The -alpine variant, not koalaman/shellcheck:stable. That one is built
|
||||
# FROM scratch with shellcheck as the entrypoint and no shell at all, so a
|
||||
# job cannot run a script in it.
|
||||
container:
|
||||
image: koalaman/shellcheck-alpine:stable@sha256:c82fe42504fbc9fc68f15d36638e5ee2324ebb8b94e96a3c4e395bf361c49183 # stable
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# Reproduces the settings ludeeus/action-shellcheck was given on GitHub:
|
||||
# severity=warning, gcc format, every script in one run.
|
||||
- run: |
|
||||
files=$(find . -name '*.sh' -not -path './.git/*' | sort)
|
||||
[ -n "$files" ] || { echo "no shell scripts found"; exit 1; }
|
||||
echo "$files" | tr '\n' ' '
|
||||
shellcheck --severity=warning --format=gcc $files
|
||||
|
||||
# One commit status on Gitea for the whole run. Gitea's ci.yml `github` job
|
||||
# waits on exactly this context.
|
||||
report:
|
||||
if: ${{ always() && vars.BUILD_ON == 'github' }}
|
||||
needs: [pending, shellcheck]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
CONTEXT: github/ci (${{ github.ref_type }})
|
||||
STATE: ${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) && 'failure' || 'success' }}
|
||||
run: |
|
||||
jq -n --arg s "$STATE" --arg c "$CONTEXT" --arg u "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
|
||||
'{state:$s, context:$c, target_url:$u, description:"GitHub Actions"}' |
|
||||
curl -fsS -o /dev/null -X POST -H "Authorization: token $GITEA_TOKEN" -H 'Content-Type: application/json' \
|
||||
--data @- "$GITEA_URL/api/v1/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA"
|
||||
@@ -1,34 +0,0 @@
|
||||
name: ShellCheck
|
||||
|
||||
# Every file in this repository is a shell script that people are
|
||||
# invited to run as root, so a lint gate is the cheapest guard against
|
||||
# the class of bug that keeps showing up here: unquoted expansions,
|
||||
# values interpolated into commands, and exit statuses that go unchecked.
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
name: ShellCheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
with:
|
||||
# warning, not error: the first run at this level surfaced
|
||||
# exactly two findings and both were fixed, so there is no
|
||||
# pre-existing backlog to grandfather in. Anything new that
|
||||
# trips it is genuinely new. Dropping to the default (info)
|
||||
# would also pull in style suggestions across every script --
|
||||
# worth doing, but as its own pass.
|
||||
severity: warning
|
||||
check_together: 'yes'
|
||||
format: gcc
|
||||
Reference in new issue
Block a user