Skip to content

Commit ec9aafb

Browse files
Young-Lordgithub-actions-sync
authored andcommitted
update about
0 parents  commit ec9aafb

330 files changed

Lines changed: 19604 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.commitlintrc.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module.exports = {
2+
parserPreset: 'conventional-changelog-conventionalcommits',
3+
rules: {
4+
'body-leading-blank': [1, 'always'],
5+
'footer-leading-blank': [1, 'always'],
6+
'header-max-length': [2, 'always', 72],
7+
'scope-case': [2, 'always', 'lower-case'],
8+
'subject-case': [
9+
2,
10+
'never',
11+
['sentence-case', 'start-case', 'pascal-case', 'upper-case']
12+
],
13+
'subject-empty': [2, 'never'],
14+
'subject-full-stop': [2, 'never', '.'],
15+
'type-case': [2, 'always', 'lower-case'],
16+
'type-empty': [2, 'never'],
17+
'type-enum': [
18+
2,
19+
'always',
20+
[
21+
'build',
22+
'chore',
23+
'ci',
24+
'docs',
25+
'feat',
26+
'fix',
27+
'improvement',
28+
'perf',
29+
'refactor',
30+
'release',
31+
'revert',
32+
'style',
33+
'test'
34+
]
35+
]
36+
}
37+
};

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# http://editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
end_of_line = lf
7+
indent_size = 2
8+
indent_style = space
9+
insert_final_newline = true
10+
max_line_length = 80
11+
trim_trailing_whitespace = true

.eslintrc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"jquery": true,
5+
"node": true
6+
},
7+
"rules": {
8+
"no-console": "error",
9+
"semi": ["error", "always"],
10+
"quotes": ["error", "single"],
11+
"comma-dangle": ["error", "never"],
12+
"block-scoped-var": "error",
13+
"default-case": "error",
14+
"no-extra-bind": "error",
15+
"camelcase": "error",
16+
"indent": ["error", 2, { "SwitchCase": 1 }],
17+
"eol-last": ["error", "always"]
18+
},
19+
"extends": ["eslint:recommended"]
20+
}

.github/workflows/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/poetry.lock
2+
/pyproject.toml

.github/workflows/jekyll.yml

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
2+
name: Deploy Jekyll site to Pages
3+
4+
on:
5+
push:
6+
workflow_dispatch:
7+
8+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
9+
permissions:
10+
contents: read
11+
pages: write
12+
id-token: write
13+
14+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
15+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
16+
concurrency:
17+
group: "pages"
18+
cancel-in-progress: false
19+
20+
jobs:
21+
# Build job
22+
build:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
- name: Setup Ruby
28+
uses: ruby/setup-ruby@v1
29+
with:
30+
ruby-version: "3.1" # Not needed with a .ruby-version file
31+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
32+
cache-version: 0 # Increment this number if you need to re-download cached gems
33+
- name: Setup Pages
34+
id: pages
35+
uses: actions/configure-pages@v4
36+
- name: Setup IndexNow
37+
run: echo ${{ secrets.INDEXNOW_KEY }} > IndexNow-${{ secrets.INDEXNOW_KEY }}.txt
38+
- name: Build with Jekyll
39+
# Outputs to the './_site' directory by default
40+
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
41+
env:
42+
JEKYLL_ENV: production
43+
- name: Upload artifact
44+
# Automatically uploads an artifact from the './_site' directory by default
45+
uses: actions/upload-pages-artifact@v3
46+
47+
# Deployment job
48+
deploy:
49+
environment:
50+
name: github-pages
51+
url: ${{ steps.deployment.outputs.page_url }}
52+
runs-on: ubuntu-latest
53+
needs: build
54+
steps:
55+
- name: Deploy to GitHub Pages
56+
id: deployment
57+
uses: actions/deploy-pages@v4
58+
59+
# Push to IndexNow
60+
indexnow:
61+
if: github.repository == 'Young-Lord/Young-Lord.github.io'
62+
runs-on: ubuntu-latest
63+
needs: deploy
64+
steps:
65+
- name: IndexNow Action
66+
uses: bojieyang/indexnow-action@v2
67+
with:
68+
sitemap-location: "https://young-lord.github.io/sitemap.xml"
69+
key: ${{ secrets.INDEXNOW_KEY }}
70+
key-location: https://young-lord.github.io/IndexNow-${{ secrets.INDEXNOW_KEY }}.txt
71+
72+
archive-page:
73+
name: Save posts to Wayback Machine
74+
runs-on: ubuntu-latest
75+
needs: deploy
76+
if: github.event_name != 'workflow_dispatch'
77+
steps:
78+
# setup python
79+
- name: Set up Python
80+
uses: actions/setup-python@v5
81+
with:
82+
python-version: "3.11"
83+
# install python packages
84+
- name: Install packages
85+
run: |
86+
python -m pip install --upgrade pip
87+
pip install requests pyyaml
88+
# find all modified files under `_posts` folder
89+
- name: Checkout
90+
uses: actions/checkout@v4
91+
- name: Find modified posts
92+
id: changed-posts
93+
uses: tj-actions/changed-files@v42
94+
with:
95+
json: true
96+
files: |
97+
_posts/*.md
98+
about.md
99+
memories.md
100+
quotepath: false
101+
safe_output: false
102+
escape_json: false
103+
since_last_remote_commit: true
104+
# use all_changed_files
105+
- name: Save Page
106+
if: steps.changed-posts.outputs.all_changed_files != '[]' && steps.changed-posts.outputs.all_changed_files != ''
107+
run: python .github/workflows/save_page.py
108+
env:
109+
all_changed_files: ${{ steps.changed-posts.outputs.all_changed_files }}
110+
SAVEPAGENOW_ACCESS_KEY: ${{ secrets.SAVEPAGENOW_ACCESS_KEY }}
111+
SAVEPAGENOW_SECRET_KEY: ${{ secrets.SAVEPAGENOW_SECRET_KEY }}

.github/workflows/save_page.py

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
"""
2+
Push changed posts to archive.org
3+
To use, modify:
4+
1. BASEURL
5+
2. Internet Archive API Key, get it at https://archive.org/account/s3.php
6+
and set them as SAVEPAGENOW_ACCESS_KEY and SAVEPAGENOW_SECRET_KEY environment variables.
7+
It's recommended to set them as repository secrets.
8+
"""
9+
10+
import json
11+
import os
12+
import traceback
13+
from typing import Final, Optional, TypedDict
14+
import yaml
15+
import requests
16+
17+
ROOTURL: Final[str] = "https://young-lord.github.io/"
18+
POSTS_BASEURL: Final[str] = ROOTURL + "posts/"
19+
20+
# https://archive.org/details/spn-2-public-api-page-docs-2023-01-22
21+
# https://github.com/palewire/savepagenow/blob/main/savepagenow/api.py MIT License
22+
DEFAULT_USER_AGENT: Final[str] = (
23+
"savepagenow (https://github.com/Young-Lord/Young-Lord.github.io/blob/master/.github/workflows/save_page.py)"
24+
)
25+
26+
27+
class WaybackRuntimeError(Exception):
28+
"""A generic error returned by the Wayback Machine."""
29+
30+
pass
31+
32+
33+
class BlockedByRobots(WaybackRuntimeError):
34+
"""Raised when archive.org has been blocked by the site's robots.txt."""
35+
36+
pass
37+
38+
39+
class BadGateway(WaybackRuntimeError):
40+
"""Raised when you receive a 502 bad gateway status code."""
41+
42+
pass
43+
44+
45+
class Unauthorized(WaybackRuntimeError):
46+
"""Raised when you receive a 401 unauthorized status code."""
47+
48+
pass
49+
50+
51+
class Forbidden(WaybackRuntimeError):
52+
"""Raised when you receive a 403 forbidden status code."""
53+
54+
pass
55+
56+
57+
class TooManyRequests(WaybackRuntimeError):
58+
"""Raised when you have exceeded the throttle on request frequency."""
59+
60+
pass
61+
62+
63+
class UnknownError(WaybackRuntimeError):
64+
"""Raised when you receive a 520 unknown status code."""
65+
66+
pass
67+
68+
69+
class WebArchiveReturn(TypedDict):
70+
url: str
71+
job_id: str
72+
73+
74+
class WebArchiveReturnWithMessage(WebArchiveReturn):
75+
message: str
76+
77+
78+
WebArchiveReturnWithMaybeMessage = WebArchiveReturnWithMessage | WebArchiveReturn
79+
80+
81+
def capture(
82+
target_url: str,
83+
authenticate: bool = True,
84+
headers: Optional[dict] = None,
85+
data: Optional[dict] = None,
86+
) -> WebArchiveReturnWithMaybeMessage:
87+
# Put together the URL that will save our request
88+
domain = "https://web.archive.org"
89+
request_url = domain + "/save"
90+
91+
if headers is None:
92+
headers = {}
93+
user_headers = headers
94+
if data is None:
95+
data = {}
96+
user_data = data
97+
98+
headers = {
99+
"User-Agent": DEFAULT_USER_AGENT,
100+
"Accept": "application/json",
101+
"Content-Type": "application/x-www-form-urlencoded",
102+
}
103+
104+
data = {
105+
"url": target_url,
106+
}
107+
data.update(user_data)
108+
109+
# Access Keys for Internet Archive API
110+
# Get it at https://archive.org/account/s3.php
111+
if authenticate:
112+
access_key = os.getenv("SAVEPAGENOW_ACCESS_KEY")
113+
secret_key = os.getenv("SAVEPAGENOW_SECRET_KEY")
114+
try:
115+
assert access_key and secret_key
116+
except AssertionError:
117+
raise ValueError(
118+
"You must set SAVEPAGENOW_ACCESS_KEY and SAVEPAGENOW_SECRET_KEY environment variables to use the authenticate flag"
119+
)
120+
headers.update(
121+
{
122+
"Authorization": f"LOW {access_key}:{secret_key}",
123+
}
124+
)
125+
126+
headers.update(user_headers)
127+
128+
# Make the request
129+
response = requests.post(request_url, headers=headers, data=data)
130+
131+
# If it has an error header, raise that.
132+
has_error_header = "X-Archive-Wayback-Runtime-Error" in response.headers
133+
if has_error_header:
134+
error_header = response.headers["X-Archive-Wayback-Runtime-Error"]
135+
if error_header == "RobotAccessControlException: Blocked By Robots":
136+
raise BlockedByRobots("archive.org returned blocked by robots.txt error")
137+
else:
138+
raise WaybackRuntimeError(error_header)
139+
140+
# If it has an error code, raise that
141+
status_code = response.status_code
142+
if status_code == 401:
143+
raise Unauthorized("Your archive.org access key and/or secret is not valid")
144+
elif status_code == 403:
145+
raise Forbidden(response.headers)
146+
elif status_code == 429:
147+
traceback.print_exc()
148+
# raise TooManyRequests(response.headers)
149+
elif status_code == 502:
150+
raise BadGateway(response.headers)
151+
elif status_code == 520:
152+
raise UnknownError(response.headers)
153+
return response.json()
154+
155+
156+
all_changed_files: list[str] = json.loads(os.environ["all_changed_files"])
157+
158+
if not all_changed_files:
159+
print("No blog post changed.")
160+
exit(0)
161+
162+
FILE_SUFFIX: Final[str] = ".md"
163+
for file in all_changed_files:
164+
assert file.endswith(FILE_SUFFIX)
165+
# https://stackoverflow.com/a/34727830
166+
url: str = ""
167+
with open(file, "r", encoding="utf8") as f:
168+
front_matter = next(yaml.load_all(f, Loader=yaml.FullLoader))
169+
title: str = front_matter["title"]
170+
if file.startswith("_posts/"):
171+
slug: str = front_matter["slug"]
172+
url = POSTS_BASEURL + slug
173+
else:
174+
assert "/" not in file # it must be in root dir
175+
url = ROOTURL + file.removesuffix(FILE_SUFFIX)
176+
177+
ret = capture(
178+
url,
179+
data={
180+
"capture_outlinks": 1,
181+
"skip_first_archive": 1,
182+
"capture_screenshot": 1,
183+
"delay_wb_availability": 1,
184+
},
185+
)
186+
print(f'"{title}" ({url}):')
187+
if "message" in ret:
188+
print("\t" + ret["message"])
189+
else:
190+
print("\tno message (that means success)")

0 commit comments

Comments
 (0)