Skip to content

Commit cd3c749

Browse files
committed
feat(seo): add Open Graph and Twitter Card support to all blog posts
- Update config.yaml with twitter handle (@hype_markdown) - Add author_twitter frontmatter to all 7 blog posts - Pin Dockerfile to hype v0.8.0 which includes enhanced OG/Twitter tags - Fix tweet.sh OAuth signing to use Python for reliability New meta tags now generated: og:site_name, article:published_time, article:author, article:tag, twitter:site, twitter:creator. Ref #4
1 parent 8612097 commit cd3c749

10 files changed

Lines changed: 72 additions & 47 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
FROM golang:1.25 AS builder
2-
RUN go install github.com/gopherguides/hype/cmd/hype@main
2+
RUN go install github.com/gopherguides/hype/cmd/hype@v0.8.0
33

44
FROM golang:1.25
55
COPY --from=builder /go/bin/hype /usr/local/bin/hype

config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ baseURL: "https://hypemd.dev"
44
author:
55
name: "Gopher Guides"
66
email: ""
7-
twitter: ""
7+
twitter: "@hype_markdown"
88
theme: "developer"
99
highlight:
1010
style: "monokai"

content/ai-authoring-workflow/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: ai-authoring-workflow
55
published: 03/15/2026
66
author: Cory LaNou
7+
author_twitter: @caborundrum
78
seo_description: Learn how to combine AI coding assistants like Claude with Hype's build-time validation to write technical documentation faster while keeping every code example correct.
89
tags: tutorial, ai, authoring, workflow, claude, hype
910
tweet: AI can write docs fast, but how do you trust the code examples? Combine Claude with Hype's build-time validation — every snippet is verified before publish.

content/deploying-with-docker/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: deploying-with-docker
55
published: 03/15/2026
66
author: Cory LaNou
7+
author_twitter: @caborundrum
78
seo_description: Deploy a Hype-powered blog site with Docker. Covers Dockerfile setup, Dokploy, Heroku, and generic VPS deployment with Docker Compose.
89
tags: tutorial, docker, deployment, blog, hype
910
tweet: Deploy a Hype-powered blog with Docker — from Dockerfile to production. Covers Dokploy, Heroku, and Docker Compose setups.

content/engineering-handbook/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: engineering-handbook
55
published: 03/15/2026
66
author: Cory LaNou
7+
author_twitter: @caborundrum
78
seo_description: Use Hype to build an internal engineering handbook that stays in sync with your codebase. Executable examples, automated validation, and single-source documentation for your team.
89
tags: tutorial, engineering, handbook, automation, hype
910
tweet: Your engineering handbook is probably wrong. Not because it was written badly — the code just changed. Here's how to make docs a build artifact with Hype.

content/getting-started/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: getting-started
55
published: 03/15/2026
66
author: Gopher Guides
7+
author_twitter: @hype_markdown
78
seo_description: Learn how to install Hype, create your first dynamic Markdown document, and execute code blocks at build time.
89
tags: tutorial, getting-started, hype
910
tweet: What if your Markdown could execute code and catch errors before you publish? Get started with Hype — dynamic Markdown for Go developers.

content/release-notes-pipeline/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: release-notes-pipeline
55
published: 03/15/2026
66
author: Cory LaNou
7+
author_twitter: @caborundrum
78
seo_description: Build a release notes pipeline using Hype with executable code snippets, automated validation, and version-aware documentation that ships with every release.
89
tags: tutorial, release-notes, ci-cd, pipeline, hype
910
tweet: Bad release notes erode trust. Build a pipeline where every code example compiles, every command runs, and every API ref is verified at build time.

content/single-source-docs/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: single-source-docs
55
published: 03/15/2026
66
author: Cory LaNou
7+
author_twitter: @caborundrum
78
seo_description: Learn how to use Hype to maintain a single Markdown source that generates both your GitHub README and website documentation, keeping them permanently in sync.
89
tags: tutorial, docs, workflow, hype
910
tweet: Your README says one thing, your docs site says another. Write it once with Hype and generate both from a single source.

content/training-materials/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
slug: training-materials
55
published: 03/15/2026
66
author: Cory LaNou
7+
author_twitter: @caborundrum
78
seo_description: Learn how to use Hype to build reusable training and course materials with executable code examples, file includes, and modular content that stays in sync with your codebase.
89
tags: tutorial, training, education, includes, hype
910
tweet: Training materials go stale the moment code changes. Use Hype to build courses with executable examples that stay in sync with your codebase.

marketing/tweet.sh

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -81,50 +81,68 @@ for var in X_API_KEY X_API_SECRET X_ACCESS_TOKEN X_ACCESS_TOKEN_SECRET; do
8181
fi
8282
done
8383

84-
urlencode() {
85-
local string="$1"
86-
python3 -c "import urllib.parse; print(urllib.parse.quote('$string', safe=''))" 2>/dev/null \
87-
|| printf '%s' "$string" | curl -Gso /dev/null -w '%{url_effective}' --data-urlencode @- '' | cut -c3-
84+
python3 - "$full_tweet" << 'PYEOF'
85+
import urllib.parse, hashlib, hmac, base64, time, os, json, secrets, sys, subprocess
86+
87+
tweet = sys.argv[1]
88+
api_key = os.environ["X_API_KEY"]
89+
api_secret = os.environ["X_API_SECRET"]
90+
access_token = os.environ["X_ACCESS_TOKEN"]
91+
access_token_secret = os.environ["X_ACCESS_TOKEN_SECRET"]
92+
93+
url = "https://api.x.com/2/tweets"
94+
95+
oauth_nonce = secrets.token_hex(16)
96+
oauth_timestamp = str(int(time.time()))
97+
98+
params = {
99+
"oauth_consumer_key": api_key,
100+
"oauth_nonce": oauth_nonce,
101+
"oauth_signature_method": "HMAC-SHA1",
102+
"oauth_timestamp": oauth_timestamp,
103+
"oauth_token": access_token,
104+
"oauth_version": "1.0",
88105
}
89106
90-
generate_nonce() {
91-
openssl rand -hex 16
92-
}
93-
94-
oauth_timestamp=$(date +%s)
95-
oauth_nonce=$(generate_nonce)
96-
97-
oauth_consumer_key="$X_API_KEY"
98-
oauth_token="$X_ACCESS_TOKEN"
99-
oauth_signature_method="HMAC-SHA1"
100-
oauth_version="1.0"
101-
102-
escaped_tweet=$(urlencode "$full_tweet")
103-
104-
param_string="oauth_consumer_key=${oauth_consumer_key}&oauth_nonce=${oauth_nonce}&oauth_signature_method=${oauth_signature_method}&oauth_timestamp=${oauth_timestamp}&oauth_token=${oauth_token}&oauth_version=${oauth_version}"
105-
106-
signature_base="POST&$(urlencode "$API_URL")&$(urlencode "$param_string")"
107-
108-
signing_key="$(urlencode "$X_API_SECRET")&$(urlencode "$X_ACCESS_TOKEN_SECRET")"
109-
110-
oauth_signature=$(printf '%s' "$signature_base" | openssl dgst -sha1 -hmac "$signing_key" -binary | base64)
111-
112-
auth_header="OAuth oauth_consumer_key=\"${oauth_consumer_key}\", oauth_nonce=\"${oauth_nonce}\", oauth_signature=\"$(urlencode "$oauth_signature")\", oauth_signature_method=\"${oauth_signature_method}\", oauth_timestamp=\"${oauth_timestamp}\", oauth_token=\"${oauth_token}\", oauth_version=\"${oauth_version}\""
113-
114-
response=$(curl -s -w "\n%{http_code}" -X POST "$API_URL" \
115-
-H "Authorization: ${auth_header}" \
116-
-H "Content-Type: application/json" \
117-
-d "{\"text\": $(printf '%s' "$full_tweet" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}")
118-
119-
http_code=$(echo "$response" | tail -1)
120-
body=$(echo "$response" | sed '$d')
121-
122-
if [[ "$http_code" == "201" ]]; then
123-
tweet_id=$(echo "$body" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['data']['id'])" 2>/dev/null || echo "unknown")
124-
echo "Tweet posted successfully!"
125-
echo "https://x.com/hype_markdown/status/${tweet_id}"
126-
else
127-
echo "Error posting tweet (HTTP ${http_code}):" >&2
128-
echo "$body" >&2
129-
exit 1
130-
fi
107+
param_string = "&".join(
108+
f"{urllib.parse.quote(k, safe='')}={urllib.parse.quote(v, safe='')}"
109+
for k, v in sorted(params.items())
110+
)
111+
signature_base = f"POST&{urllib.parse.quote(url, safe='')}&{urllib.parse.quote(param_string, safe='')}"
112+
signing_key = f"{urllib.parse.quote(api_secret, safe='')}&{urllib.parse.quote(access_token_secret, safe='')}"
113+
signature = base64.b64encode(
114+
hmac.new(signing_key.encode(), signature_base.encode(), hashlib.sha1).digest()
115+
).decode()
116+
117+
auth_header = (
118+
f'OAuth oauth_consumer_key="{urllib.parse.quote(api_key, safe="")}", '
119+
f'oauth_nonce="{urllib.parse.quote(oauth_nonce, safe="")}", '
120+
f'oauth_signature="{urllib.parse.quote(signature, safe="")}", '
121+
f'oauth_signature_method="HMAC-SHA1", '
122+
f'oauth_timestamp="{oauth_timestamp}", '
123+
f'oauth_token="{urllib.parse.quote(access_token, safe="")}", '
124+
f'oauth_version="1.0"'
125+
)
126+
127+
result = subprocess.run(
128+
["curl", "-s", "-w", "\n%{http_code}", "-X", "POST", url,
129+
"-H", f"Authorization: {auth_header}",
130+
"-H", "Content-Type: application/json",
131+
"-d", json.dumps({"text": tweet})],
132+
capture_output=True, text=True
133+
)
134+
135+
output = result.stdout.strip()
136+
http_code = output.split("\n")[-1]
137+
body = "\n".join(output.split("\n")[:-1])
138+
139+
if http_code == "201":
140+
data = json.loads(body)
141+
tweet_id = data["data"]["id"]
142+
print("Tweet posted successfully!")
143+
print(f"https://x.com/hype_markdown/status/{tweet_id}")
144+
else:
145+
print(f"Error posting tweet (HTTP {http_code}):", file=sys.stderr)
146+
print(body, file=sys.stderr)
147+
sys.exit(1)
148+
PYEOF

0 commit comments

Comments
 (0)