mirror of
https://github.com/ZSCGR/CloudFlare-ImgBed.git
synced 2026-08-13 04:03:42 +08:00
250 lines
8.8 KiB
YAML
250 lines
8.8 KiB
YAML
# Render with pinned official source and persist date/count history in Pages.
|
|
# The workflow deploys Pages artifacts only; it never creates repository commits.
|
|
name: Render Star History
|
|
|
|
on:
|
|
schedule:
|
|
- cron: "17 3 * * 1"
|
|
workflow_dispatch:
|
|
inputs:
|
|
force_bootstrap:
|
|
description: "Rebuild all Star History data from the GitHub API instead of the deployed Pages cache"
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
contents: read
|
|
deployments: write
|
|
pages: write
|
|
id-token: write
|
|
|
|
concurrency:
|
|
group: star-history-pages
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
STAR_HISTORY_REPOSITORIES: marseventh/cloudflare-imgbed,marseventh/sanyue-imghub
|
|
STAR_HISTORY_SOURCE_REF: bcddc9d532b10bac7e0187a741288bf9cab17616
|
|
STAR_HISTORY_SOURCE_DIR: .star-history-upstream
|
|
STAR_HISTORY_SITE_DIR: .star-history-site
|
|
STAR_HISTORY_PAGES_URL: https://marseventh.github.io/CloudFlare-ImgBed
|
|
|
|
jobs:
|
|
render-and-deploy:
|
|
name: Render with official source and deploy to Pages
|
|
if: ${{ github.repository == 'MarSeventh/CloudFlare-ImgBed' }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
environment:
|
|
name: github-pages
|
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
steps:
|
|
- name: Check out this repository
|
|
uses: actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Check out official Star History source
|
|
uses: actions/checkout@v7
|
|
with:
|
|
repository: star-history/star-history
|
|
ref: ${{ env.STAR_HISTORY_SOURCE_REF }}
|
|
path: ${{ env.STAR_HISTORY_SOURCE_DIR }}
|
|
persist-credentials: false
|
|
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@v6
|
|
with:
|
|
version: 9.15.9
|
|
run_install: false
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version: "24"
|
|
cache: pnpm
|
|
cache-dependency-path: |
|
|
.star-history-upstream/pnpm-lock.yaml
|
|
.star-history-upstream/backend/pnpm-lock.yaml
|
|
|
|
- name: Install official renderer dependencies
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
(cd "${STAR_HISTORY_SOURCE_DIR}" && pnpm install --frozen-lockfile)
|
|
(cd "${STAR_HISTORY_SOURCE_DIR}/backend" && pnpm install --frozen-lockfile)
|
|
|
|
- name: Refresh data and render official light/dark charts locally
|
|
id: render
|
|
shell: bash
|
|
env:
|
|
# Normal refreshes read the deployed Pages cache and current repository
|
|
# totals. A missing Pages cache is initialized once from timestamped
|
|
# stargazers; the hosted Star History chart service is never queried.
|
|
GITHUB_JOB_TOKEN: ${{ github.token }}
|
|
FORCE_BOOTSTRAP: ${{ github.event_name == 'workflow_dispatch' && inputs.force_bootstrap == true }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [[ -z "${GITHUB_JOB_TOKEN}" ]]; then
|
|
echo "::error::The workflow GITHUB_TOKEN is unavailable."
|
|
exit 1
|
|
fi
|
|
|
|
data_file="${RUNNER_TEMP}/star-history-data.json"
|
|
server_log="${RUNNER_TEMP}/star-history-backend.log"
|
|
|
|
cleanup() {
|
|
if [[ -n "${server_pid:-}" ]]; then
|
|
kill "${server_pid}" 2>/dev/null || true
|
|
wait "${server_pid}" 2>/dev/null || true
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
refresh_args=(
|
|
--repository "${STAR_HISTORY_REPOSITORIES}"
|
|
--output "${data_file}"
|
|
--token-env GITHUB_JOB_TOKEN
|
|
)
|
|
|
|
# Pages JSON is normally the runtime source of truth. A first
|
|
# deployment or an explicitly forced bootstrap reconstructs history
|
|
# from GitHub's timestamped Stargazers API.
|
|
if [[ "${FORCE_BOOTSTRAP}" == "true" ]]; then
|
|
echo "[data] forced API bootstrap requested"
|
|
else
|
|
refresh_args+=(
|
|
--deployed-url "${STAR_HISTORY_PAGES_URL}/star-history-data.json"
|
|
)
|
|
fi
|
|
|
|
python3 .github/star-history/star_history.py refresh-data \
|
|
"${refresh_args[@]}"
|
|
|
|
# Seed the pinned official backend cache while leaving its JSDOM,
|
|
# XYChart, xkcd styling, theme handling, and SVGO renderer unchanged.
|
|
python3 .github/star-history/star_history.py patch-upstream \
|
|
--source-dir "${STAR_HISTORY_SOURCE_DIR}"
|
|
(cd "${STAR_HISTORY_SOURCE_DIR}/backend" && pnpm run build)
|
|
|
|
(
|
|
cd "${STAR_HISTORY_SOURCE_DIR}/backend"
|
|
STAR_HISTORY_DATA_PATH="${data_file}" pnpm exec tsx main.ts
|
|
) >"${server_log}" 2>&1 &
|
|
server_pid=$!
|
|
|
|
for attempt in {1..60}; do
|
|
if curl --fail --silent http://127.0.0.1:8080/healthz >/dev/null; then
|
|
break
|
|
fi
|
|
if ! kill -0 "${server_pid}" 2>/dev/null; then
|
|
cat "${server_log}" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${attempt}" == 60 ]]; then
|
|
cat "${server_log}" >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
mkdir -p "${STAR_HISTORY_SITE_DIR}"
|
|
cp "${data_file}" "${STAR_HISTORY_SITE_DIR}/star-history-data.json"
|
|
if ! python3 .github/star-history/star_history.py render \
|
|
--repository "${STAR_HISTORY_REPOSITORIES}" \
|
|
--output-dir "${STAR_HISTORY_SITE_DIR}" \
|
|
--fallback-base-url "${STAR_HISTORY_PAGES_URL}"; then
|
|
cat "${server_log}" >&2
|
|
exit 1
|
|
fi
|
|
touch "${STAR_HISTORY_SITE_DIR}/.nojekyll"
|
|
|
|
(
|
|
cd "${STAR_HISTORY_SITE_DIR}"
|
|
sha256sum \
|
|
star-history-data.json \
|
|
star-history-dark.svg \
|
|
star-history-light.svg | LC_ALL=C sort > manifest.sha256
|
|
)
|
|
|
|
# A byte-stable same-day count skips the Pages deployment entirely.
|
|
current_dir="$(mktemp -d)"
|
|
changed=true
|
|
if curl --fail --silent --location \
|
|
"${STAR_HISTORY_PAGES_URL}/manifest.sha256" \
|
|
--output "${current_dir}/manifest.sha256" && \
|
|
cmp --silent \
|
|
"${STAR_HISTORY_SITE_DIR}/manifest.sha256" \
|
|
"${current_dir}/manifest.sha256"; then
|
|
changed=false
|
|
echo "The deployed Pages site is already current."
|
|
fi
|
|
echo "changed=${changed}" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: Configure GitHub Pages
|
|
if: steps.render.outputs.changed == 'true'
|
|
uses: actions/configure-pages@v6
|
|
|
|
- name: Upload Pages artifact
|
|
if: steps.render.outputs.changed == 'true'
|
|
uses: actions/upload-pages-artifact@v5
|
|
with:
|
|
path: ${{ env.STAR_HISTORY_SITE_DIR }}
|
|
|
|
- name: Deploy Pages artifact
|
|
if: steps.render.outputs.changed == 'true'
|
|
id: deployment
|
|
uses: actions/deploy-pages@v5
|
|
|
|
# Deployment records are not Git commits. Keep only the newest record so
|
|
# the repository's Deployments list does not grow every scheduled run.
|
|
- name: Delete old GitHub Pages deployment records
|
|
if: ${{ success() && steps.render.outcome == 'success' }}
|
|
uses: actions/github-script@v9
|
|
with:
|
|
github-token: ${{ github.token }}
|
|
script: |
|
|
const { owner, repo } = context.repo;
|
|
const environment = "github-pages";
|
|
const deployments = await github.paginate(
|
|
github.rest.repos.listDeployments,
|
|
{ owner, repo, environment, per_page: 100 },
|
|
);
|
|
|
|
deployments.sort((left, right) => {
|
|
const difference = Date.parse(right.created_at) - Date.parse(left.created_at);
|
|
return difference || right.id - left.id;
|
|
});
|
|
if (deployments.length <= 1) {
|
|
core.info(`Keeping ${deployments.length} ${environment} deployment record(s).`);
|
|
return;
|
|
}
|
|
|
|
const [currentDeployment, ...oldDeployments] = deployments;
|
|
for (const oldDeployment of oldDeployments) {
|
|
const { data: statuses } = await github.rest.repos.listDeploymentStatuses({
|
|
owner,
|
|
repo,
|
|
deployment_id: oldDeployment.id,
|
|
per_page: 1,
|
|
});
|
|
if (statuses[0]?.state !== "inactive") {
|
|
await github.rest.repos.createDeploymentStatus({
|
|
owner,
|
|
repo,
|
|
deployment_id: oldDeployment.id,
|
|
state: "inactive",
|
|
environment,
|
|
auto_inactive: false,
|
|
description: `Superseded by deployment ${currentDeployment.id}.`,
|
|
});
|
|
}
|
|
await github.rest.repos.deleteDeployment({
|
|
owner,
|
|
repo,
|
|
deployment_id: oldDeployment.id,
|
|
});
|
|
}
|