An automated multi-platform blog publishing system.
Write blog posts in Japanese, auto-translate to English, and deploy to multiple platforms automatically.
- Draft Creation: Create blog posts from templates via GitHub Actions, automatically generating Pull Requests
- Preview: Auto-deploy preview sites for each PR
- Article Management: Unpublish or delete published articles
- Multi-platform Deployment:
- π―π΅ Japanese: Qiita, Zenn, note
- πΊπΈ English: dev.to, Medium, daily.dev
βββ draft_entries/
β βββ ja/ # Japanese drafts
β βββ en/ # English drafts (auto-generated)
βββ entries/
β βββ ja/ # Published Japanese articles
β βββ en/ # Published English articles
βββ templates/ # Article templates
βββ images/ # Article images (auto-uploaded to Cloudflare Images)
β βββ yyyy/mm/dd/ # Date-based directories
βββ .github/
βββ templates/ # Preview HTML templates
βββ workflows/ # GitHub Actions workflows
Choose from the following templates when creating a draft:
| Template | Description | Use Case |
|---|---|---|
| tech-article | Technical articles | Technical explanations, knowledge sharing |
| tutorial | Tutorial format | Hands-on, step-by-step guides |
| review | Reviews | Product/service/tool reviews |
| retrospective | Retrospectives | Monthly reviews, project retrospectives |
| news | Announcements | Release notes, announcements |
| til | Today I Learned | Daily learnings, small discoveries |
Add new Markdown files to the templates/ directory to create custom templates.
Available variables in templates:
${TITLE}- Article title${DATE}- Creation date (YYYY-MM-DD)${SLUG}- Article slug (used for Stack Overflow tags, etc.)
To use the preview feature, enable GitHub Pages:
- Go to repository Settings > Pages
- Select
gh-pagesbranch as Source - Click Save
Configure the following secrets in Settings > Secrets and variables > Actions.
| Secret Name | Description | How to Obtain |
|---|---|---|
OPENAI_API_KEY |
OpenAI API Key (for translation) | OpenAI Platform ![]() |
PAT_TOKEN |
Personal Access Token (for sample repo creation/deletion) | GitHub Settings with repo permission ![]() |
CLOUDFLARE_ACCOUNT_ID |
Cloudflare Account ID | Cloudflare Dashboard URL ![]() |
CLOUDFLARE_API_TOKEN |
Cloudflare API Token | API Tokens with Images edit permission ![]() |
CLOUDFLARE_ACCOUNT_HASH |
Cloudflare Images Account Hash | Cloudflare Dashboard > Images > Overview ![]() |
QIITA_ACCESS_TOKEN |
Qiita Access Token | Qiita Settings with read_qiita and write_qiita enabled ![]() |
NOTE_EMAIL |
note Login Email | Your note account |
NOTE_PASSWORD |
note Login Password | Your note account |
DEVTO_API_KEY |
dev.to API Key | dev.to Settings API Keys section ![]() |
MEDIUM_ACCESS_TOKEN |
Medium Integration Token | Medium Settings Integration tokens ![]() |
MEDIUM_USER_ID |
Medium User ID | Via API (see below) |
curl -H "Authorization: Bearer YOUR_MEDIUM_ACCESS_TOKEN" \
https://api.medium.com/v1/meThe data.id in the response is your User ID.
Zenn uses GitHub repository integration for automatic deployment.
- Log in to Zenn
- Configure GitHub integration from Deploy Settings

- Link this repository
daily.dev discovers articles via RSS feeds or other platforms.
- Articles published on dev.to or Medium are automatically discovered
- To register a custom RSS feed: https://daily.dev/submit-source
Create labels from repository Settings > Labels.
| Label Name | Color | Description |
|---|---|---|
draft |
#fbca04 |
π Draft article |
published |
#0e8a16 |
π Published article |
needs-review |
#d93f0b |
π Needs review |
wip |
#1d76db |
π§ Work in progress |
japanese |
#bc002d |
π―π΅ Japanese article |
english |
#012169 |
πΊπΈ English article |
When added to a PR, these labels are auto-synced to the article's categories.
| Label Name | Color | Description |
|---|---|---|
javascript |
#f1e05a |
JavaScript |
typescript |
#3178c6 |
TypeScript |
react |
#61dafb |
React |
nextjs |
#000000 |
Next.js |
nodejs |
#339933 |
Node.js |
python |
#3776ab |
Python |
devops |
#326ce5 |
DevOps |
github-actions |
#2088ff |
GitHub Actions |
docker |
#2496ed |
Docker |
aws |
#ff9900 |
AWS |
database |
#336791 |
Database |
security |
#d73a4a |
Security |
performance |
#7057ff |
Performance |
testing |
#0e8a16 |
Testing |
career |
#e99695 |
Career |
learning |
#bfdadc |
Learning / TIL |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. Run "Create Blog Draft and PR" from Actions β
β β Select template β Create draft β Auto-generate PR β
β β (Optional) Auto-create sample repository β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. Write while checking preview site β
β β Add images β Auto-upload to Cloudflare Images β
β β Add labels β Auto-sync to categories β
β β CI (textlint, markdownlint, cspell, etc.) quality check β
β β Auto-translate to English β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. Review approval β Article status changes to "publishable" β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 4. Merge PR β Auto-deploy to each platform β
β Japanese β Qiita, Zenn, note β
β English β dev.to, Medium, daily.dev β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Open the Actions tab
- Select Create Blog Draft
- Click Run workflow
- Enter the following:
- Blog Title: Article title
- Slug: For filename (alphanumeric and hyphens)
- Template: Select according to article type
- Create sample repository: Check if you need a sample code repository
- Sample repository template: Specify template repository (leave empty for blank repo)
- Check the created Pull Request
- Preview URL will be posted in comments - write while checking it
- Edit Markdown files in
draft_entries/ja/ - Add images to
images/YYYY-MM-DD/(auto-uploaded to CDN) - Add labels to PR (auto-synced to categories)
- On push, automatically:
- Preview site updates
- CI (textlint, markdownlint, cspell, prettier) quality check
- Auto-translate to English (saved to
draft_entries/en/)
- Check both Japanese and English content in preview
- Fix CI errors
- When you Approve the review, article status automatically changes to "publishable"
When you merge the PR, only articles with draft: false are automatically deployed to:
- π―π΅ Japanese: Qiita, Zenn, note
- πΊπΈ English: dev.to, Medium, daily.dev
You can temporarily unpublish a published article and move it back to drafts.
- Open the Actions tab
- Select Unpublish Article (Move to Draft)
- Enter the following:
- article_path: Article path (e.g.,
entries/ja/2024-01-01-my-article.md) - reason: Reason for unpublishing (optional)
- article_path: Article path (e.g.,
What happens:
- Moves from
entries/todraft_entries/ - Changes front matter
draft: falseβdraft: true - Changes Qiita article to private
- Changes dev.to article to draft
You can delete an article and all related resources.
- Open the Actions tab
- Select Delete Article
- Enter the following:
- article_path: Article path
- delete_sample_repo: Check to also delete sample repository
- confirm: Type
DELETE(deletion confirmation)
What gets deleted:
- Article files (both Japanese and English)
- Local image files
- Cloudflare Images
- Platform articles (Qiita, dev.to)
- Sample repository (optional)
β οΈ This operation cannot be undone.
Questions about articles are accepted on Stack Overflow.
Each article has a Stack Overflow tag (the article slug). Use that tag when asking questions.
| Workflow | Trigger | Description |
|---|---|---|
create-draft.yaml |
Manual (workflow_dispatch) | Create draft from template, create PR, create sample repo |
upload-images.yaml |
PR (images/**) | Upload images to Cloudflare Images |
sync-labels.yaml |
PR (labeled/unlabeled) | Sync labels to categories |
preview.yaml |
PR (draft_entries/**) | Deploy preview site |
translate-draft.yaml |
PR (draft_entries/ja/**) | Japanese β English translation |
publish-draft.yaml |
PR review approval | Change draft β publishable |
| Workflow | Trigger | Description |
|---|---|---|
ci.yml |
PR (draft_entries/**) | Quality check with textlint, markdownlint, cspell, prettier |
| Workflow | Trigger | Description |
|---|---|---|
deploy-japanese.yaml |
push to master | Deploy to Qiita, Zenn, note |
deploy-english.yaml |
push to master | Deploy to dev.to, Medium, daily.dev |
| Workflow | Trigger | Description |
|---|---|---|
unpublish-article.yaml |
Manual (workflow_dispatch) | Unpublish article and move to drafts |
delete-article.yaml |
Manual (workflow_dispatch) | Delete article and related resources |
The following checks run automatically when you create a PR:
| Check | Tool | Description |
|---|---|---|
| Japanese text | textlint | Technical writing checks for Japanese |
| Markdown syntax | markdownlint | Markdown syntax and style checks |
| Spelling | cspell | English spelling checks |
| Formatting | prettier | Code format checks |
npm run lintEdit files in the templates/ directory or add new templates.
Edit the system prompt in translate-draft.yaml to customize translation style.
To add new platforms, add jobs to the corresponding workflow files.
Edit the systemLabels array in sync-labels.yaml to set labels that don't sync to categories.
| Config File | Description |
|---|---|
.textlintrc |
textlint rule settings |
.markdownlint.json |
markdownlint rule settings |
.cspell.json |
cspell dictionary and exclusion settings |
.prettierrc |
prettier format settings |
- Check if GitHub Pages is enabled
- Check if
gh-pagesbranch exists
- Check if Cloudflare secrets are correctly configured
- Check if image files are placed in
images/directory
- Check if labels are added to PR
- Check if labels are not system labels (
draft,blog, etc.)
- Check if
OPENAI_API_KEYis correctly configured - Check if changed files are in
draft_entries/ja/
- Check if API keys/tokens for each platform are correct
- Check if API limits have been reached
- Check if article status is
draft: false
- Check if
PAT_TOKENis correctly configured - Check if token has
repopermission
- Check error details and fix the relevant parts
- Adjust config files if no fix is needed
Feel free to open Issues or Pull Requests.










