Skip to content

feat: add secure repository-owned star history #3

feat: add secure repository-owned star history

feat: add secure repository-owned star history #3

name: Update Star History
on:
# TEMPORARY: delete this push trigger and the feature-smoke job after the
# feature/monthly-star-history workflow has passed its one-time smoke run.
push:
branches:
- feature/monthly-star-history
schedule:
- cron: '17 3 * * *'
workflow_dispatch:
inputs:
force:
description: Record a snapshot even when 15 days have not elapsed
required: false
default: false
type: boolean
permissions:
contents: read
concurrency:
group: bettafish-star-history
cancel-in-progress: false
jobs:
feature-smoke:
# TEMPORARY: read-only one-shot validation for feature/monthly-star-history.
if: >-
${{
github.event_name == 'push' &&
github.repository == '666ghj/BettaFish' &&
github.ref == 'refs/heads/feature/monthly-star-history' &&
github.actor_id == '110395318' &&
github.triggering_actor == '666ghj'
}}
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Checkout exact feature commit without persisted credentials
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 1
persist-credentials: false
- name: Run Star History tests without tokens
env:
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: >-
python3 -m unittest
tests.test_star_history
tests.test_fetch_star_count
-v
- name: Fetch aggregate Star count only
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
umask 077
output="$RUNNER_TEMP/bettafish-star-count.txt"
[[ ! -e "$output" && ! -L "$output" ]]
python3 scripts/fetch_star_count.py > "$output"
[[ -f "$output" && ! -L "$output" ]]
(( $(wc -c < "$output") <= 32 ))
mapfile -t lines < "$output"
(( ${#lines[@]} == 1 ))
[[ "${lines[0]}" =~ ^[0-9]+$ ]]
- name: Force record and render offline without tokens
shell: bash
env:
STAR_COUNT_FILE: ${{ runner.temp }}/bettafish-star-count.txt
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: |
set -euo pipefail
trap 'rm -f -- "$STAR_COUNT_FILE"' EXIT
[[ -z "${GITHUB_TOKEN:-}" && -z "${GH_TOKEN:-}" ]]
python3 scripts/star_history.py record \
--count-file "$STAR_COUNT_FILE" \
--force
- name: Verify smoke outputs without tokens
env:
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: |
python3 scripts/star_history.py check
python3 -m unittest \
tests.test_star_history \
tests.test_fetch_star_count \
-v
due-check:
if: >-
${{
github.repository == '666ghj/BettaFish' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
}}
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
outputs:
due: ${{ steps.due.outputs.due }}
steps:
- name: Checkout triggering commit
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 1
persist-credentials: false
- name: Check whether a snapshot is due
id: due
shell: bash
run: |
set -euo pipefail
value="$(python3 scripts/star_history.py due)"
case "$value" in
true|false) ;;
*)
echo "::error::Invalid due result"
exit 1
;;
esac
printf 'due=%s\n' "$value" >> "$GITHUB_OUTPUT"
update-main:
needs: due-check
if: >-
${{
needs.due-check.result == 'success' &&
github.repository == '666ghj/BettaFish' &&
github.ref == 'refs/heads/main' &&
(
github.event_name == 'schedule' ||
(
github.event_name == 'workflow_dispatch' &&
github.actor_id == '110395318' &&
github.triggering_actor == '666ghj'
)
) &&
(
needs.due-check.outputs.due == 'true' ||
(github.event_name == 'workflow_dispatch' && inputs.force == true)
)
}}
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: write
env:
GIT_TERMINAL_PROMPT: '0'
steps:
- name: Checkout triggering commit
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 1
persist-credentials: false
- name: Run Star History tests without tokens
env:
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: >-
python3 -m unittest
tests.test_star_history
tests.test_fetch_star_count
-v
- name: Fetch aggregate Star count only
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
umask 077
output="$RUNNER_TEMP/bettafish-star-count.txt"
[[ ! -e "$output" && ! -L "$output" ]]
python3 scripts/fetch_star_count.py > "$output"
[[ -f "$output" && ! -L "$output" ]]
(( $(wc -c < "$output") <= 32 ))
mapfile -t lines < "$output"
(( ${#lines[@]} == 1 ))
[[ "${lines[0]}" =~ ^[0-9]+$ ]]
- name: Record aggregate Star snapshot offline without tokens
if: ${{ github.event_name != 'workflow_dispatch' || inputs.force != true }}
shell: bash
env:
STAR_COUNT_FILE: ${{ runner.temp }}/bettafish-star-count.txt
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: |
set -euo pipefail
trap 'rm -f -- "$STAR_COUNT_FILE"' EXIT
[[ -z "${GITHUB_TOKEN:-}" && -z "${GH_TOKEN:-}" ]]
python3 scripts/star_history.py record --count-file "$STAR_COUNT_FILE"
- name: Force aggregate Star snapshot offline without tokens
if: ${{ github.event_name == 'workflow_dispatch' && inputs.force == true }}
shell: bash
env:
STAR_COUNT_FILE: ${{ runner.temp }}/bettafish-star-count.txt
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: |
set -euo pipefail
trap 'rm -f -- "$STAR_COUNT_FILE"' EXIT
[[ -z "${GITHUB_TOKEN:-}" && -z "${GH_TOKEN:-}" ]]
python3 scripts/star_history.py record \
--count-file "$STAR_COUNT_FILE" \
--force
- name: Verify generated outputs without tokens
env:
GITHUB_TOKEN: ''
GH_TOKEN: ''
run: |
python3 scripts/star_history.py check
python3 -m unittest \
tests.test_star_history \
tests.test_fetch_star_count \
-v
- name: Commit exact output allowlist
id: commit
shell: bash
run: |
set -euo pipefail
allowed() {
case "$1" in
.github/star-history/history.json|\
static/image/star-history-light.svg|\
static/image/star-history-dark.svg) return 0 ;;
*) return 1 ;;
esac
}
bad=0
while IFS= read -r -d '' path; do
if ! allowed "$path"; then
printf '::error::Unexpected changed path: %q\n' "$path"
bad=1
fi
done < <(
git diff --name-only -z
git diff --cached --name-only -z
git ls-files --others --exclude-standard -z
)
(( bad == 0 )) || exit 1
for path in \
.github/star-history/history.json \
static/image/star-history-light.svg \
static/image/star-history-dark.svg
do
[[ -f "$path" && ! -L "$path" && -s "$path" ]] || {
printf '::error::Invalid output file: %s\n' "$path"
exit 1
}
[[ "$(realpath -e -- "$path")" == "$GITHUB_WORKSPACE/$path" ]] || {
printf '::error::Output escaped workspace: %s\n' "$path"
exit 1
}
done
git add -- \
.github/star-history/history.json \
static/image/star-history-light.svg \
static/image/star-history-dark.svg
if git diff --cached --quiet; then
printf 'created=false\n' >> "$GITHUB_OUTPUT"
exit 0
fi
count=0
while IFS= read -r -d '' path; do
allowed "$path" || exit 1
((count += 1))
done < <(git diff --cached --name-only -z)
(( count > 0 )) || exit 1
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git -c commit.gpgsign=false commit \
-m 'chore: update star history [skip ci]'
printf 'created=true\n' >> "$GITHUB_OUTPUT"
- name: Verify one allowlisted commit and unchanged main target
if: ${{ steps.commit.outputs.created == 'true' }}
shell: bash
run: |
set -euo pipefail
allowed() {
case "$1" in
.github/star-history/history.json|\
static/image/star-history-light.svg|\
static/image/star-history-dark.svg) return 0 ;;
*) return 1 ;;
esac
}
base="$GITHUB_SHA"
target_ref='refs/heads/main'
[[ "$GITHUB_REPOSITORY" == '666ghj/BettaFish' ]]
[[ "$GITHUB_REF" == "$target_ref" ]]
[[ "$(git rev-parse HEAD^)" == "$base" ]]
[[ "$(git rev-list --count "${base}..HEAD")" == 1 ]]
[[ -z "$(git status --porcelain --untracked-files=all)" ]]
origin="$(git remote get-url origin)"
case "$origin" in
https://github.com/666ghj/BettaFish|\
https://github.com/666ghj/BettaFish.git) ;;
*)
echo "::error::Unexpected origin"
exit 1
;;
esac
mapfile -t push_urls < <(git remote get-url --push --all origin)
(( ${#push_urls[@]} == 1 ))
[[ "${push_urls[0]}" == "$origin" ]]
count=0
while IFS= read -r -d '' path; do
allowed "$path" || {
printf '::error::Unexpected committed path: %q\n' "$path"
exit 1
}
((count += 1))
done < <(git diff-tree --no-commit-id --name-only -r -z HEAD)
(( count > 0 )) || exit 1
git fetch --no-tags --depth=1 origin "$target_ref"
[[ "$(git rev-parse FETCH_HEAD)" == "$base" ]] || {
echo "::error::Target advanced; refusing to rebase or overwrite"
exit 1
}
- name: Push one allowlisted commit with an ephemeral credential
if: ${{ steps.commit.outputs.created == 'true' }}
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
GIT_TERMINAL_PROMPT: '0'
GIT_TRACE: '0'
GIT_TRACE_CURL: '0'
GIT_TRACE_PACKET: '0'
GIT_CURL_VERBOSE: '0'
run: |
set -euo pipefail
allowed() {
case "$1" in
.github/star-history/history.json|\
static/image/star-history-light.svg|\
static/image/star-history-dark.svg) return 0 ;;
*) return 1 ;;
esac
}
base="$GITHUB_SHA"
[[ "$GITHUB_REPOSITORY" == '666ghj/BettaFish' ]]
[[ "$GITHUB_REF" == 'refs/heads/main' ]]
[[ "$(git rev-parse HEAD^)" == "$base" ]]
[[ "$(git rev-list --count "${base}..HEAD")" == 1 ]]
[[ -z "$(git status --porcelain --untracked-files=all)" ]]
origin="$(git remote get-url origin)"
case "$origin" in
https://github.com/666ghj/BettaFish|\
https://github.com/666ghj/BettaFish.git) ;;
*)
echo "::error::Unexpected origin"
exit 1
;;
esac
mapfile -t push_urls < <(git remote get-url --push --all origin)
(( ${#push_urls[@]} == 1 ))
[[ "${push_urls[0]}" == "$origin" ]]
count=0
while IFS= read -r -d '' path; do
allowed "$path" || {
printf '::error::Unexpected committed path: %q\n' "$path"
exit 1
}
((count += 1))
done < <(git diff-tree --no-commit-id --name-only -r -z HEAD)
(( count > 0 )) || exit 1
[[ -n "$GITHUB_TOKEN" ]]
[[ "$GITHUB_TOKEN" != *$'\n'* && "$GITHUB_TOKEN" != *$'\r'* ]]
encoded="$(
printf 'x-access-token:%s' "$GITHUB_TOKEN" |
base64 |
tr -d '\n'
)"
export GIT_CONFIG_COUNT=1
export GIT_CONFIG_KEY_0="http.${origin}.extraheader"
export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic $encoded"
unset encoded GITHUB_TOKEN
trap 'unset GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0' EXIT
git push --porcelain origin HEAD:refs/heads/main