├── .github ├── release.yml ├── workflows │ ├── push-published-entries.yaml │ ├── _release.yaml │ ├── push.yaml │ ├── push-draft.yaml │ ├── pull.yaml │ ├── create-draft.yaml │ ├── pull-draft.yaml │ ├── upload-images.yaml │ ├── initialize.yaml │ └── push-when-publishing-from-draft.yaml └── actions │ ├── setup │ └── action.yaml │ ├── move-draft-and-update-metadata │ └── action.yaml │ └── create-draft-pull-request │ └── action.yaml ├── renovate.json ├── scripts ├── README.md └── update-revision.bash ├── LICENSE ├── README.md ├── .tagpr ├── fotolife-client.py └── CHANGELOG.md /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - tagpr 5 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>hatena/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # scripts 2 | 3 | ## update-revision.bash 4 | 5 | hatena/hatenablog-workflowsのGitHub Actionsを自己参照している箇所のリビジョンを更新するスクリプトで、hatena/hatenablog-workflows開発用のファイルです。 6 | 7 | 新しいリリースを打つ前に、手元のgitリポジトリを最新にして `./scripts/update-revision.bash` を実行したあと、`update-hatenablog-workflows-digest` ブランチの内容をmainに(Pull Request経由で)取り込んでください 8 | -------------------------------------------------------------------------------- /.github/workflows/push-published-entries.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] push published entries" 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | OWNER_API_KEY: 7 | required: true 8 | 9 | jobs: 10 | upload-images: 11 | if: github.event.pull_request.merged == false 12 | uses: hatena/hatenablog-workflows/.github/workflows/upload-images.yaml@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 13 | secrets: 14 | OWNER_API_KEY: ${{ secrets.OWNER_API_KEY }} 15 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yaml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: setup git-restore-mtime 7 | run: | 8 | sudo apt-get install -y git-restore-mtime 9 | shell: bash 10 | - name: setup blogsync 11 | uses: x-motemen/blogsync@9760a4c471a07ca7d471dd9f09ecbb258f6da6b0 # v0.20.1 12 | with: 13 | version: v0.20.1 14 | - name: restore mtime 15 | run: | 16 | git restore-mtime 17 | shell: bash 18 | -------------------------------------------------------------------------------- /scripts/update-revision.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | BRANCH_NAME=update-hatenablog-workflows-digest 6 | 7 | git rev-parse --verify "$BRANCH_NAME" 2>/dev/null && git branch -d "$BRANCH_NAME" 8 | git switch -c "$BRANCH_NAME" 9 | 10 | current_reference=$(git grep -h -oP '(?<=hatena/hatenablog-workflows/.github/actions/setup@)([0-9a-f]+)' | head -1) 11 | current_revision=$(git show --format='%H' --no-patch) 12 | git grep --name-only "$current_reference" | xargs -L 1 -I@ gsed -i @ -e "s/$current_reference/$current_revision/g" 13 | 14 | git add .github 15 | git commit -m "update hatenablog-workflows digest" 16 | -------------------------------------------------------------------------------- /.github/actions/move-draft-and-update-metadata/action.yaml: -------------------------------------------------------------------------------- 1 | name: move draft and update metadata 2 | 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: move draft and update metadata 7 | run: | 8 | draft_files=($(grep -xl 'Draft: true' $(git ls-files -mo --exclude-standard) || echo "")) 9 | if [[ ${#draft_files[@]} -eq 0 ]]; then 10 | exit 0 11 | fi 12 | for file in ${draft_files[@]}; do 13 | entry_id=$(yq --front-matter=extract '.EditURL' "$file" | grep -oP '[^/]+\d$') 14 | yq --front-matter=process -i 'del(.Date,.URL)' "$file" 15 | mv "$file" "draft_entries/$entry_id.${file##*.}" 16 | done 17 | shell: bash 18 | -------------------------------------------------------------------------------- /.github/workflows/_release.yaml: -------------------------------------------------------------------------------- 1 | name: Release with tagpr 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | 7 | jobs: 8 | tagpr: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 12 | id: app-token 13 | with: 14 | app-id: ${{ vars.APP_ID }} 15 | private-key: ${{ secrets.PRIVATE_KEY }} 16 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 17 | with: 18 | token: ${{ steps.app-token.outputs.token }} 19 | - uses: Songmu/tagpr@7191605433b03e11b313dbbc0efb80185170de4b # v1.9.0 20 | env: 21 | GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Hatena Co., Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/push.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] push to hatena blog" 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | local_root: 7 | default: "entries" 8 | type: string 9 | secrets: 10 | OWNER_API_KEY: 11 | required: true 12 | 13 | jobs: 14 | push: 15 | if: | 16 | github.event.pull_request.merged == true 17 | && !contains(github.event.pull_request.labels.*.name, 'skip-push') 18 | runs-on: ubuntu-latest 19 | env: 20 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 21 | steps: 22 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 23 | with: 24 | fetch-depth: 0 25 | - name: setup 26 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 27 | - name: Get changed files 28 | id: changed-files 29 | uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 30 | with: 31 | files: ${{ inputs.local_root }}/**/*.md 32 | since_last_remote_commit: true 33 | - name: blogsync push 34 | run: | 35 | for file in ${{ steps.changed-files.outputs.all_changed_files }}; do 36 | draft=$(yq --front-matter=extract 'select(.Draft == true)' "$file") 37 | if [[ -z "$draft" ]]; then 38 | blogsync push "$file" 39 | fi 40 | done 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hatenablog-workflows 2 | 3 | ## Usage 4 | 5 | サンプル用の[Template リポジトリ](https://github.com/hatena/Hatena-Blog-Workflows-Boilerplate)をご確認ください 6 | 7 | ## Workflows 8 | 9 | ### `initialize.yaml` 10 | 11 | - 初めてはてなブログと GitHub を連携するときに利用するアクションです 12 | - はてなブログからすべての記事データ(公開済みおよび下書き)を取得します 13 | - 公開記事は`entries`ディレクトリ, 下書き記事は`draft_entries`ディレクトリにそれぞれ配置されます 14 | - `Date`および`URL`は公開時に設定されることを想定しているため, 下書き記事ではこれらのメタデータは削除した状態で配置されます 15 | - 本アクションで作成されたプルリクエストはマージしてもはてなブログへの同期(push)は行われません 16 | 17 | ### `create-draft.yaml` 18 | 19 | - 下書きファイルを新規作成するときに利用するアクションです 20 | - タイトルの文字列を受け取って記事の新規作成, はてなブログへの同期とプルリクエストの作成を行います 21 | 22 | ### `pull-draft.yaml` 23 | 24 | - はてなブログから特定のタイトルの下書き記事を取得するときに利用するアクションです 25 | - 引数にタイトルを渡して実行してください 26 | - 同一の下書きタイトルが複数ある場合, 意図しない挙動になる可能性がありますので一意の名前をつけてください 27 | - タイトルと一致する下書き記事が取得できると, `draft_entries`に当該の下書き記事ファイルが追加されたプルリクエストが作成されます 28 | 29 | ### `pull.yaml` 30 | 31 | - はてなブログから公開済みの記事のみを取得するアクションです 32 | - `entries`配下に当該記事ファイルが追加, 更新されたプルリクエストが作成されます 33 | - 新たに追加, 更新のあった場合のみが対象です 34 | - 下書きファイルは取得対象外になっています 35 | - 本アクションで作成されたプルリクエストはマージしてもはてなブログへの同期(push)は行われません 36 | 37 | ### `push-draft.yaml` 38 | 39 | - 下書きファイルの更新結果をはてなブログに同期するためのアクションです 40 | - `draft_entries`にある下書き記事の場合のみ, はてなブログに同期します 41 | - `draft_entries`に対して変更があるたびに実行することを想定しております 42 | 43 | ### `push-when-publishing-from-draft.yaml` 44 | 45 | - 下書きファイルを初めて公開するときに利用するアクションです 46 | - `draft_entries`にある下書き記事を公開する変更があった場合, はてなブログへ記事を公開します 47 | - また, 同期した記事を`entries`に移動するプルリクエストを作成および自動マージします 48 | - main ブランチにマージされたときにのみ実行することを想定しています 49 | 50 | ### `push.yaml` 51 | 52 | - 公開済みの記事を更新し, はてなブログに反映するときに利用するアクションです 53 | - `entries`内の更新のあった記事をはてなブログに同期します 54 | - main ブランチにマージされたときにのみ実行することを想定しています 55 | -------------------------------------------------------------------------------- /.github/workflows/push-draft.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] push draft to hatena blog" 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | OWNER_API_KEY: 7 | required: true 8 | 9 | jobs: 10 | upload-images: 11 | uses: hatena/hatenablog-workflows/.github/workflows/upload-images.yaml@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 12 | secrets: 13 | OWNER_API_KEY: ${{ secrets.OWNER_API_KEY }} 14 | push-draft: 15 | if: always() 16 | needs: upload-images 17 | runs-on: ubuntu-latest 18 | env: 19 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 20 | steps: 21 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 22 | with: 23 | ref: ${{ needs.upload-images.result == 'success' && needs.upload-images.outputs.revision || '' }} 24 | fetch-depth: 0 25 | - name: setup 26 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 27 | - name: Get changed draft files 28 | id: changed-draft-files 29 | uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 30 | with: 31 | files: draft_entries/**/*.md 32 | - name: push only draft entry 33 | run: | 34 | for file in ${{ steps.changed-draft-files.outputs.all_changed_files }}; do 35 | draft=$(yq --front-matter=extract 'select(.Draft == true)' "$file") 36 | editurl=$(yq --front-matter=extract 'select(.EditURL == "https://blog.hatena.ne.jp/*")' "$file") 37 | if [[ -n "$draft" ]] && [[ -n "$editurl" ]]; then 38 | blogsync push "$file" 39 | fi 40 | done 41 | -------------------------------------------------------------------------------- /.github/workflows/pull.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] pull from hatenablog" 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | BLOG_DOMAIN: 7 | required: true 8 | type: string 9 | secrets: 10 | OWNER_API_KEY: 11 | required: true 12 | 13 | jobs: 14 | pull: 15 | runs-on: ubuntu-latest 16 | env: 17 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 18 | steps: 19 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 20 | with: 21 | fetch-depth: 0 22 | - name: setup 23 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 24 | - name: set blog domain 25 | id: set-domain 26 | run: | 27 | domain=${{ inputs.BLOG_DOMAIN }} 28 | echo "BLOG_DOMAIN=$(echo $domain | tr -d '\n\r ')" >> "$GITHUB_OUTPUT" 29 | - name: pull 30 | run: | 31 | blogsync pull ${{ steps.set-domain.outputs.BLOG_DOMAIN }} 32 | - name: delete draft files 33 | run: | 34 | target=$(git ls-files -mo --exclude-standard | xargs grep -xl 'Draft: true') 35 | if [[ -n "$target" ]]; then 36 | IFS=" " read -r -a draft_files <<< "$(echo "$target" | xargs)" 37 | for file in "${draft_files[@]}"; do 38 | rm "$file" 39 | done 40 | fi 41 | - name: create pull request 42 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 43 | with: 44 | title: blogsync pull 45 | branch: blogsync-pull 46 | commit-message: | 47 | blogsync pull 48 | body: | 49 | blogsync pull 50 | labels: | 51 | skip-push 52 | delete-branch: true 53 | -------------------------------------------------------------------------------- /.tagpr: -------------------------------------------------------------------------------- 1 | # config file for the tagpr in git config format 2 | # The tagpr generates the initial configuration, which you can rewrite to suit your environment. 3 | # CONFIGURATIONS: 4 | # tagpr.releaseBranch 5 | # Generally, it is "main." It is the branch for releases. The tagpr tracks this branch, 6 | # creates or updates a pull request as a release candidate, or tags when they are merged. 7 | # 8 | # tagpr.versionFile 9 | # Versioning file containing the semantic version needed to be updated at release. 10 | # It will be synchronized with the "git tag". 11 | # Often this is a meta-information file such as gemspec, setup.cfg, package.json, etc. 12 | # Sometimes the source code file, such as version.go or Bar.pm, is used. 13 | # If you do not want to use versioning files but only git tags, specify the "-" string here. 14 | # You can specify multiple version files by comma separated strings. 15 | # 16 | # tagpr.vPrefix 17 | # Flag whether or not v-prefix is added to semver when git tagging. (e.g. v1.2.3 if true) 18 | # This is only a tagging convention, not how it is described in the version file. 19 | # 20 | # tagpr.changelog (Optional) 21 | # Flag whether or not changelog is added or changed during the release. 22 | # 23 | # tagpr.command (Optional) 24 | # Command to change files just before release. 25 | # 26 | # tagpr.template (Optional) 27 | # Pull request template in go template format 28 | # 29 | # tagpr.release (Optional) 30 | # GitHub Release creation behavior after tagging [true, draft, false] 31 | # If this value is not set, the release is to be created. 32 | # 33 | # tagpr.majorLabels (Optional) 34 | # Label of major update targets. Default is [major] 35 | # 36 | # tagpr.minorLabels (Optional) 37 | # Label of minor update targets. Default is [minor] 38 | # 39 | [tagpr] 40 | vPrefix = true 41 | releaseBranch = main 42 | versionFile = - 43 | -------------------------------------------------------------------------------- /.github/workflows/create-draft.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] create draft and pull from hatenablog" 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | title: 7 | required: true 8 | type: string 9 | draft: 10 | default: true 11 | type: boolean 12 | BLOG_DOMAIN: 13 | required: true 14 | type: string 15 | secrets: 16 | OWNER_API_KEY: 17 | required: true 18 | 19 | jobs: 20 | post_draft_and_pull_from_hatenablog: 21 | name: create draft and pull from hatenablog 22 | runs-on: ubuntu-latest 23 | env: 24 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 25 | steps: 26 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 27 | with: 28 | fetch-depth: 0 29 | - name: setup 30 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 31 | - name: setup draft 32 | run: | 33 | yq --front-matter="process" ".Title=strenv(TITLE)" draft.template > draft 34 | env: 35 | TITLE: ${{ inputs.title }} 36 | - name: set blog domain 37 | id: set-domain 38 | run: | 39 | domain=${{ inputs.BLOG_DOMAIN }} 40 | echo "BLOG_DOMAIN=$(echo $domain | tr -d '\n\r ')" >> "$GITHUB_OUTPUT" 41 | - name: post draft to hatenablog 42 | id: post-draft 43 | run: | 44 | entry_path=$(blogsync post --draft ${{ steps.set-domain.outputs.BLOG_DOMAIN }} < 'draft') 45 | echo "ENTRY_PATH=$entry_path" >> "$GITHUB_OUTPUT" 46 | - name: fetch entry 47 | run: | 48 | blogsync fetch ${{ steps.post-draft.outputs.ENTRY_PATH }} 49 | - name: pull draft by title 50 | uses: hatena/hatenablog-workflows/.github/actions/create-draft-pull-request@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 51 | with: 52 | title: ${{ inputs.title }} 53 | draft: ${{ inputs.draft }} 54 | BLOG_DOMAIN: ${{ steps.set-domain.outputs.BLOG_DOMAIN }} 55 | ENTRY_PATH: ${{ steps.post-draft.outputs.ENTRY_PATH }} 56 | -------------------------------------------------------------------------------- /.github/workflows/pull-draft.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] pull draft entry from hatenablog" 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | title: 7 | required: true 8 | type: string 9 | draft: 10 | default: true 11 | type: boolean 12 | BLOG_DOMAIN: 13 | required: true 14 | type: string 15 | secrets: 16 | OWNER_API_KEY: 17 | required: true 18 | 19 | jobs: 20 | pull-draft: 21 | runs-on: ubuntu-latest 22 | env: 23 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 24 | ENTRY_TITLE: ${{ inputs.title }} 25 | steps: 26 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 27 | with: 28 | fetch-depth: 0 29 | - name: setup 30 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 31 | - name: set blog domain 32 | id: set-domain 33 | run: | 34 | domain=${{ inputs.BLOG_DOMAIN }} 35 | echo "BLOG_DOMAIN=$(echo $domain | tr -d '\n\r ')" >> "$GITHUB_OUTPUT" 36 | - name: pull 37 | run: | 38 | blogsync pull ${{ steps.set-domain.outputs.BLOG_DOMAIN }} 39 | - name: set entry path 40 | id: set-entry-path 41 | run: | 42 | files=($(git ls-files -o --exclude-standard)) 43 | for file in ${files[@]}; do 44 | title=$(yq --front-matter=extract '.Title' "$file") 45 | if [[ "$title" == "$ENTRY_TITLE" ]]; then 46 | entry_path="$file" 47 | fi 48 | done 49 | if [[ -z "$entry_path" ]]; then 50 | echo "Error: No draft entry titled ${ENTRY_TITLE} was found" 51 | exit 1 52 | fi 53 | echo "ENTRY_PATH=$entry_path" >> $GITHUB_OUTPUT 54 | - name: pull draft by title 55 | uses: hatena/hatenablog-workflows/.github/actions/create-draft-pull-request@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 56 | with: 57 | title: ${{ inputs.title }} 58 | draft: ${{ inputs.draft }} 59 | BLOG_DOMAIN: ${{ steps.set-domain.outputs.BLOG_DOMAIN }} 60 | ENTRY_PATH: ${{ steps.set-entry-path.outputs.ENTRY_PATH }} 61 | -------------------------------------------------------------------------------- /.github/workflows/upload-images.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] upload images" 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | OWNER_API_KEY: 7 | required: true 8 | outputs: 9 | revision: 10 | value: ${{ jobs.upload-image.outputs.revision }} 11 | 12 | jobs: 13 | upload-image: 14 | runs-on: ubuntu-latest 15 | outputs: 16 | revision: ${{ steps.commit-and-push.outputs.revision }} 17 | steps: 18 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 19 | with: 20 | ref: ${{ github.event.pull_request.head.ref }} 21 | - name: list changed entries 22 | id: changed-entries 23 | uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 24 | with: 25 | files: | 26 | **.md 27 | - name: set owner 28 | id: set-owner 29 | run: | 30 | owner=$(yq "..|.owner|select(.)" blogsync.yaml) 31 | if [[ -z "$owner" ]]; then 32 | owner=$(yq "..|.username|select(.)" blogsync.yaml) 33 | fi 34 | echo "OWNER_ID=$owner" >> $GITHUB_OUTPUT 35 | - name: Download the script 36 | run: | 37 | curl -fsSL -o /tmp/fotolife-client.py https://raw.githubusercontent.com/hatena/hatenablog-workflows/v1/fotolife-client.py 38 | chmod +x /tmp/fotolife-client.py 39 | - name: List all changed files markdown files 40 | if: steps.changed-entries.outputs.any_changed == 'true' 41 | run: | 42 | for f in ${{ steps.changed-entries.outputs.all_changed_files }}; do 43 | python3 /tmp/fotolife-client.py "$f" 44 | done 45 | env: 46 | HATENA_ID: ${{ steps.set-owner.outputs.OWNER_ID }} 47 | OWNER_API_KEY: ${{ secrets.OWNER_API_KEY }} 48 | - name: commit & push 49 | id: commit-and-push 50 | run: | 51 | git config user.name "actions-user" 52 | git config user.email "action@github.com" 53 | if [[ $(git diff) -eq 0 ]]; then 54 | exit 0 55 | fi 56 | 57 | git add . 58 | git commit -m "upload images to fotolife" 59 | git push 60 | 61 | revision=$(git rev-parse HEAD) 62 | echo "REVISION=$revision" >> $GITHUB_OUTPUT 63 | -------------------------------------------------------------------------------- /.github/workflows/initialize.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] initialize" 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | is_draft_included: 7 | default: true 8 | type: boolean 9 | BLOG_DOMAIN: 10 | required: true 11 | type: string 12 | secrets: 13 | OWNER_API_KEY: 14 | required: true 15 | 16 | jobs: 17 | initialize: 18 | env: 19 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 23 | with: 24 | fetch-depth: 0 25 | - name: setup 26 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 27 | - name: set blog domain 28 | id: set-domain 29 | run: | 30 | domain=${{ inputs.BLOG_DOMAIN }} 31 | echo "BLOG_DOMAIN=$(echo $domain | tr -d '\n\r ')" >> "$GITHUB_OUTPUT" 32 | - name: pull 33 | run: | 34 | blogsync pull ${{ steps.set-domain.outputs.BLOG_DOMAIN }} 35 | - name: move draft and update metadata 36 | if: inputs.is_draft_included == true 37 | uses: hatena/hatenablog-workflows/.github/actions/move-draft-and-update-metadata@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 38 | - name: delete draft files 39 | if: inputs.is_draft_included == false 40 | run: | 41 | target=$(git ls-files -mo --exclude-standard | xargs grep -xl 'Draft: true' || echo "") 42 | if [[ -n "$target" ]]; then 43 | IFS=" " read -r -a draft_files <<< "$(echo "$target" | xargs)" 44 | for file in "${draft_files[@]}"; do 45 | rm "$file" 46 | done 47 | fi 48 | - name: create pull request 49 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 50 | with: 51 | title: initialize 52 | branch: initialize 53 | commit-message: | 54 | initialize 55 | body: | 56 | はてなブログから取得した記事データを commit しました 57 | **${{ inputs.is_draft_included && '下書き記事を含んでいますので公開リポジトリの場合はマージは慎重に行ってください' || '公開記事のみが対象となっています'}}** 58 | labels: | 59 | skip-push 60 | delete-branch: true 61 | -------------------------------------------------------------------------------- /.github/workflows/push-when-publishing-from-draft.yaml: -------------------------------------------------------------------------------- 1 | name: "[Reusable workflows] push when publishing from draft" 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | BLOG_DOMAIN: 7 | required: true 8 | type: string 9 | secrets: 10 | OWNER_API_KEY: 11 | required: true 12 | 13 | jobs: 14 | push-when-publishing-from-draft: 15 | if: | 16 | github.event.pull_request.merged == true 17 | && !contains(github.event.pull_request.labels.*.name, 'skip-push') 18 | runs-on: ubuntu-latest 19 | env: 20 | BLOGSYNC_PASSWORD: ${{ secrets.OWNER_API_KEY }} 21 | steps: 22 | - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 23 | with: 24 | fetch-depth: 0 25 | - name: setup 26 | uses: hatena/hatenablog-workflows/.github/actions/setup@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 27 | - name: Get changed draft files 28 | id: changed-draft-files 29 | uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 30 | with: 31 | files: draft_entries/**/*.md 32 | since_last_remote_commit: true 33 | - name: blogsync push 34 | run: | 35 | for file in ${{ steps.changed-draft-files.outputs.all_changed_files }}; do 36 | draft=$(yq --front-matter=extract 'select(.Draft == true)' "$file") 37 | if [[ -z "$draft" ]]; then 38 | blogsync push "$file" 39 | fi 40 | done 41 | - name: create pull request 42 | id: cpr 43 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 44 | with: 45 | title: from draft to publish 46 | branch: from-draft-to-publish 47 | commit-message: | 48 | from draft to publish 49 | labels: | 50 | skip-push 51 | body: | 52 | はてなブログに公開したファイルを`local_root`で指定したディレクトリに移動しました 53 | delete-branch: true 54 | - name: Enable Pull Request Automerge 55 | if: steps.cpr.outputs.pull-request-operation == 'created' 56 | uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3.0.0 57 | with: 58 | pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} 59 | merge-method: squash 60 | -------------------------------------------------------------------------------- /.github/actions/create-draft-pull-request/action.yaml: -------------------------------------------------------------------------------- 1 | name: create draft pull request 2 | 3 | inputs: 4 | title: 5 | required: true 6 | draft: 7 | required: true 8 | BLOG_DOMAIN: 9 | required: true 10 | ENTRY_PATH: 11 | required: true 12 | 13 | runs: 14 | using: "composite" 15 | steps: 16 | - name: set entry variables 17 | id: set-entry-variables 18 | run: | 19 | echo "EDIT_URL=$(yq --front-matter=extract '.EditURL' ${{ inputs.ENTRY_PATH }})" >> $GITHUB_OUTPUT 20 | echo "ENTRY_ID=$(yq --front-matter=extract '.EditURL' ${{ inputs.ENTRY_PATH }} | grep -oP '[^/]+\d$')" >> $GITHUB_OUTPUT 21 | echo "PREVIEW_URL=$(yq --front-matter=extract '.PreviewURL' ${{ inputs.ENTRY_PATH }})" >> $GITHUB_OUTPUT 22 | shell: bash 23 | - name: set owner 24 | id: set-owner 25 | run: | 26 | owner=$(yq ".[\"${{ inputs.BLOG_DOMAIN }}\"].owner" blogsync.yaml) 27 | if [[ "$owner" == 'null' ]]; then 28 | owner=$(yq ".[\"${{ inputs.BLOG_DOMAIN }}\"].username" blogsync.yaml) 29 | fi 30 | echo "OWNER_NAME=$owner" >> $GITHUB_OUTPUT 31 | shell: bash 32 | - name: delete other files 33 | run: | 34 | set +eo pipefail 35 | delete_files=($(git ls-files -o --exclude-standard | xargs -r grep -xL "EditURL: ${{ steps.set-entry-variables.outputs.EDIT_URL }}")) 36 | for file in ${delete_files[@]}; do 37 | rm "$file" 38 | done 39 | 40 | restore_files=($(git ls-files -m --exclude-standard | xargs -r grep -xL "EditURL: ${{ steps.set-entry-variables.outputs.EDIT_URL }}")) 41 | for file in ${restore_files[@]}; do 42 | git restore "$file" 43 | done 44 | shell: bash 45 | - name: move draft and update metadata 46 | uses: hatena/hatenablog-workflows/.github/actions/move-draft-and-update-metadata@2c159def2fce5ebed56e62d70136a4a93a435725 # v2.0.5 47 | - name: get pull request template 48 | id: get-pull-request-template 49 | env: 50 | GH_TOKEN: ${{ github.token }} 51 | run: | 52 | pr_template=$(gh repo view --json pullRequestTemplates | jq -r '.pullRequestTemplates | [map(select(.filename == "draft.md"))[0], map(select(.filename == "PULL_REQUEST_TEMPLATE.md"))[0]] | map(select(. != null)) | first | .body // empty') 53 | if [[ -z "$pr_template" ]]; then 54 | pr_template=$(cat<<'EOF' 55 | ## ${TITLE} 56 | 57 | - 編集ページのURL: ${EDIT_URL} 58 | - プレビューへのURL: ${PREVIEW_URL} 59 | 60 | EOF 61 | ) 62 | fi 63 | echo "PULL_REQUEST_TEMPLATE<> $GITHUB_OUTPUT 64 | echo "$pr_template" >> $GITHUB_OUTPUT 65 | echo "EOF" >> $GITHUB_OUTPUT 66 | shell: bash 67 | - name: prepare pull request body 68 | id: prepare-pull-request-body 69 | env: 70 | TITLE: ${{ inputs.title }} 71 | EDIT_URL: ${{ steps.set-entry-variables.outputs.EDIT_URL }} 72 | OWNER_NAME: ${{ steps.set-owner.outputs.OWNER_NAME }} 73 | ENTRY_ID: ${{ steps.set-entry-variables.outputs.ENTRY_ID }} 74 | PREVIEW_URL: ${{ steps.set-entry-variables.outputs.PREVIEW_URL == 'null' && 'なし' || steps.set-entry-variables.outputs.PREVIEW_URL }} 75 | PR_TEMPLATE: ${{ steps.get-pull-request-template.outputs.PULL_REQUEST_TEMPLATE }} 76 | run: | 77 | echo "PULL_REQUEST_BODY<> $GITHUB_OUTPUT 78 | echo "${PR_TEMPLATE}" | envsubst '${TITLE} ${EDIT_URL} ${PREVIEW_URL} ${OWNER_NAME} ${ENTRY_ID}' >> $GITHUB_OUTPUT 79 | echo "EOF" >> $GITHUB_OUTPUT 80 | shell: bash 81 | - name: create draft pull request 82 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 83 | with: 84 | title: ${{ inputs.title }} 85 | branch: draft-entry-${{ steps.set-entry-variables.outputs.ENTRY_ID }} 86 | body: ${{ steps.prepare-pull-request-body.outputs.PULL_REQUEST_BODY }} 87 | delete-branch: true 88 | draft: ${{ inputs.draft == 'true' }} 89 | -------------------------------------------------------------------------------- /fotolife-client.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | import os 4 | from hashlib import sha1 5 | from base64 import b64encode 6 | from datetime import datetime 7 | import secrets 8 | import mimetypes 9 | from urllib.request import Request, urlopen 10 | from urllib.error import HTTPError 11 | from xml.etree import ElementTree 12 | 13 | ATOM_TEMPLATE = """ 14 | POST /atom/post 15 | 16 | 17 | uploaded by fotolife-client.py 18 | {} 19 | hatena/hatenablog-workflows 20 | 21 | """ 22 | 23 | wsse_value = "" 24 | base_dir = "" 25 | 26 | def wsse(username, api_key): 27 | """ 28 | ユーザのIDとAPIキーからX-WSSEヘッダの値を生成する 29 | """ 30 | nonce = sha1(secrets.token_bytes(16)).digest() 31 | now = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + "Z" 32 | digest = sha1(nonce + now.encode() + api_key.encode()).digest() 33 | return 'UsernameToken Username="{}", PasswordDigest="{}", Nonce="{}", Created="{}"'.format( 34 | username, 35 | b64encode(digest).decode(), 36 | b64encode(nonce).decode(), 37 | now, 38 | ) 39 | 40 | def upload_to_fotolife(path: str) -> str|None: 41 | """ 42 | 画像のパスを受け取って、その画像ファイルをFotolifeにアップロードする 43 | その画像をFotolife記法として埋め込むための文字列を返す。 44 | """ 45 | mime = mimetypes.guess_type(path)[0] 46 | with open(path, "rb") as f: 47 | imgbuf = f.read() 48 | 49 | body = ATOM_TEMPLATE.format(mime, b64encode(imgbuf).decode()) 50 | req = Request("https://f.hatena.ne.jp/atom/post", method="POST", headers={ 51 | "X-WSSE": wsse_value, 52 | }, data=body.encode()) 53 | try: 54 | with urlopen(req) as res: 55 | if not 200 <= res.status < 300: 56 | print(f"[-] failed to request: {res.url}, reason: {res.reason}") 57 | return None 58 | 59 | resbuf = res.read() 60 | tree = ElementTree.fromstring(resbuf) 61 | ns = { 62 | "hatena": "http://www.hatena.ne.jp/info/xmlns#" 63 | } 64 | syntaxes = tree.findall("hatena:syntax", ns) 65 | syntax = re.sub(r':image$', ':plain', syntaxes[0].text) 66 | print(f"[+] uploaded {path}") 67 | return syntax 68 | except HTTPError as e: 69 | print(f"[-] failed to request: {e.url}, reason: {e.reason}") 70 | return None 71 | 72 | 73 | def replace_to_fotolife_syntax(match: re.Match) -> str: 74 | """ 75 | Markdownの画像記法で参照されている画像をFotolifeにアップロードしつつ、Fotolife記法に置き換える 76 | """ 77 | alt = match.group("alt") 78 | path = match.group("path") 79 | title = match.group("quot") or match.group("squot") 80 | 81 | # pathがローカルのファイルを指していなければ何もしない 82 | path = os.path.join(base_dir, path) 83 | if not os.path.exists(path): 84 | print(f"[-] Skipped: file not found: {path}") 85 | return match[0] 86 | 87 | syntax = upload_to_fotolife(path) 88 | if syntax is None: 89 | return match[0] 90 | 91 | if alt and title: 92 | return f"[{syntax}:title={title}:alt={alt}]" 93 | elif (not alt) and title: 94 | return f"[{syntax}:title={title}]" 95 | elif alt and (not title): 96 | return f"[{syntax}:alt={alt}]" 97 | elif (not alt) and (not title): 98 | return f"[{syntax}]" 99 | 100 | def main(): 101 | """ 102 | usage: python3 fotolife-client.py 103 | """ 104 | global wsse_value 105 | global base_dir 106 | 107 | 108 | hatena_id = os.getenv("HATENA_ID") 109 | owner_api_key = os.getenv("OWNER_API_KEY") 110 | if not hatena_id or not owner_api_key: 111 | print("please set environment variables: HATENA_ID, OWNER_API_KEY") 112 | sys.exit(1) 113 | wsse_value = wsse(hatena_id, owner_api_key) 114 | 115 | if len(sys.argv) == 1: 116 | print(f"usage: python3 {sys.argv[0]} ") 117 | sys.exit(1) 118 | target = sys.argv[1] 119 | with open(target, "r", encoding="utf-8") as f: 120 | buf = f.read() 121 | base_dir = os.path.dirname(target) 122 | 123 | pattern = re.compile(r"""!\[(?P[^\]]*)\]\((?P[^\)]*?)\s*("(?P[^"]*)"\s*)?('(?P[^']*)'\s*)?\)""") 124 | res = pattern.sub(replace_to_fotolife_syntax, buf) 125 | with open(target, "w", encoding="utf-8") as f: 126 | f.write(res) 127 | 128 | if __name__ == "__main__": 129 | main() 130 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v2.0.7](https://github.com/hatena/hatenablog-workflows/compare/v2.0.6...v2.0.7) - 2025-11-19 4 | - 外部のactionのバージョンをフル指定する by @taiseiue in https://github.com/hatena/hatenablog-workflows/pull/142 5 | - Update Songmu/tagpr action to v1.9.0 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/141 6 | - Update tj-actions/changed-files action to v47 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/146 7 | - Update actions/create-github-app-token action to v2.1.4 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/145 8 | - Update actions/checkout action to v5.0.1 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/147 9 | - update hatenablog-workflows digest by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/148 10 | 11 | ## [v2.0.6](https://github.com/hatena/hatenablog-workflows/compare/v2.0.5...v2.0.6) - 2025-08-18 12 | - Update Songmu/tagpr action to v1.7.0 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/130 13 | - Update actions/create-github-app-token action to v2.0.6 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/116 14 | - Update actions/create-github-app-token action to v2.1.1 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/137 15 | - Update actions/checkout action to v4.3.0 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/138 16 | - Update actions/checkout action to v5 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/139 17 | - Update hatena/hatenablog-workflows digest to ce4c0e0 by @renovate[bot] in https://github.com/hatena/hatenablog-workflows/pull/135 18 | 19 | ## [v2.0.5](https://github.com/hatena/hatenablog-workflows/compare/v2.0.4...v2.0.5) - 2025-06-02 20 | - Update hatena/hatenablog-workflows digest to 50f103a by @renovate in https://github.com/hatena/hatenablog-workflows/pull/107 21 | 22 | ## [v2.0.4](https://github.com/hatena/hatenablog-workflows/compare/v2.0.3...v2.0.4) - 2025-06-02 23 | - Update hatena/hatenablog-workflows action to v2 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/129 24 | 25 | ## [v2.0.3](https://github.com/hatena/hatenablog-workflows/compare/v2.0.2...v2.0.3) - 2025-06-02 26 | - draft-entry のブランチに ENTRY_ID が設定されないため修正 by @nemuki in https://github.com/hatena/hatenablog-workflows/pull/131 27 | 28 | ## [v2.0.2](https://github.com/hatena/hatenablog-workflows/compare/v2.0.1...v2.0.2) - 2025-05-07 29 | - 意図しない記述が入っていたので削除 by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/127 30 | 31 | ## [v2.0.1](https://github.com/hatena/hatenablog-workflows/compare/v2.0.0...v2.0.1) - 2025-05-07 32 | - プルリクエストテンプレートに利用するファイルの名前をdraft.mdに変更 by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/126 33 | 34 | ## [v2.0.0](https://github.com/hatena/hatenablog-workflows/compare/v1.3.7...v2.0.0) - 2025-05-07 35 | - create-draft-pull-request で PULL_REQUEST_TEMPLATE.md 挿入する by @nemuki in https://github.com/hatena/hatenablog-workflows/pull/114 36 | - PRテンプレート周りの変更 by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/117 https://github.com/hatena/hatenablog-workflows/pull/119 https://github.com/hatena/hatenablog-workflows/pull/121 https://github.com/hatena/hatenablog-workflows/pull/122 https://github.com/hatena/hatenablog-workflows/pull/123 37 | - Update hatenablog workflows digest by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/124 38 | - chore(deps): update songmu/tagpr action to v1.5.2 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/113 39 | 40 | ## [v1.3.7](https://github.com/hatena/hatenablog-workflows/compare/v1.3.6...v1.3.7) - 2025-04-14 41 | - chore(deps): update actions/create-github-app-token action to v2 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/109 42 | - chore(deps): update tj-actions/changed-files action to v46.0.5 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/111 43 | - update-actions-ref-hash by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/112 44 | 45 | ## [v1.3.6](https://github.com/hatena/hatenablog-workflows/compare/v1.3.5...v1.3.6) - 2025-03-31 46 | - fix: create-draft may not work without workflow_dispatch or workflow_call event type. by @guitarrapc in https://github.com/hatena/hatenablog-workflows/pull/104 47 | - chore(deps): update tj-actions/changed-files action to v46.0.3 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/102 48 | - update reference hash by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/106 49 | 50 | ## [v1.3.5](https://github.com/hatena/hatenablog-workflows/compare/v1.3.4...v1.3.5) - 2025-03-21 51 | - Pin dependencies by @renovate in https://github.com/hatena/hatenablog-workflows/pull/98 52 | - Update actions/create-github-app-token action to v1.11.7 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/100 53 | - Update tj-actions/changed-files action to v46 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/95 54 | 55 | ## [v1.3.4](https://github.com/hatena/hatenablog-workflows/compare/v1.3.3...v1.3.4) - 2025-03-17 56 | - 外部のactionのバージョンをSHAで指定 by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/94 57 | - Update tj-actions/changed-files action to v45.0.9 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/96 58 | 59 | ## [v1.3.3](https://github.com/hatena/hatenablog-workflows/compare/v1.3.2...v1.3.3) - 2024-10-16 60 | - Update peter-evans/create-pull-request action to v7 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/87 61 | - 画像ファイルが見つからなかった時のログを表示する by @morihaya in https://github.com/hatena/hatenablog-workflows/pull/90 62 | 63 | ## [v1.3.2](https://github.com/hatena/hatenablog-workflows/compare/v1.3.1...v1.3.2) - 2024-09-10 64 | - Update tj-actions/changed-files action to v45 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/85 65 | - フォトライフ記法にplain指定子を追加 by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/88 66 | 67 | ## [v1.3.1](https://github.com/hatena/hatenablog-workflows/compare/v1.3.0...v1.3.1) - 2024-08-19 68 | - Update CHANGELOG.md by @halkt in https://github.com/hatena/hatenablog-workflows/pull/82 69 | - PullRequestをDraftで作成するかどうかを呼び出し元で選択できるようにする by @halkt in https://github.com/hatena/hatenablog-workflows/pull/84 70 | 71 | ## [v1.3.0](https://github.com/hatena/hatenablog-workflows/compare/v1.2.17...v1.3.0) - 2024-06-24 - Minor Version Up!! 72 | - 下書き用の記事のPull Requestの作成時はPull Request自体もdraft状態で作成する by @halkt in https://github.com/hatena/hatenablog-workflows/pull/80 73 | - [HatenaBlog Workflows Boilerplate で下書き記事を作成した際にドラフト状態のプルリクエストとして作成されるよう変更しました - はてなブログ開発ブログ](https://staff.hatenablog.com/entry/2024/06/25/163227) 74 | 75 | ## [v1.2.17](https://github.com/hatena/hatenablog-workflows/compare/v1.2.16...v1.2.17) - 2024-04-11 76 | - Update blogsync v0.20.1 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/77 77 | 78 | ## [v1.2.16](https://github.com/hatena/hatenablog-workflows/compare/v1.2.15...v1.2.16) - 2024-04-09 79 | - 画像アップロード時のgenerator名にリポジトリを入れておく by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/75 80 | - upload-images action を各workflowに組み込む by @halkt in https://github.com/hatena/hatenablog-workflows/pull/69 81 | 82 | ## [v1.2.15](https://github.com/hatena/hatenablog-workflows/compare/v1.2.14...v1.2.15) - 2024-04-03 83 | - chore(deps): update peter-evans/create-pull-request action to v6 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/67 84 | - chore(deps): update tj-actions/changed-files action to v43 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/70 85 | - fotolife-client.pyの取得するcurlコマンドにオプション追加 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/71 86 | - changed-draft-filesのfileをmdファイルに限定する by @halkt in https://github.com/hatena/hatenablog-workflows/pull/73 87 | - chore(deps): update tj-actions/changed-files action to v44 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/72 88 | 89 | ## [v1.2.14](https://github.com/hatena/hatenablog-workflows/compare/v1.2.13...v1.2.14) - 2024-01-26 90 | - blogsyncのバージョンをv0.18.2に戻す by @halkt in https://github.com/hatena/hatenablog-workflows/pull/65 91 | 92 | ## [v1.2.13](https://github.com/hatena/hatenablog-workflows/compare/v1.2.12...v1.2.13) - 2024-01-25 93 | - chore(deps): update tj-actions/changed-files action to v42 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/62 94 | - 下書き作成(create-draft-action)時にblogsync fetchを使うように変更する by @halkt in https://github.com/hatena/hatenablog-workflows/pull/64 95 | 96 | ## [v1.2.12](https://github.com/hatena/hatenablog-workflows/compare/v1.2.11...v1.2.12) - 2024-01-17 97 | - pull-draft.yamlのjob名を修正 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/58 98 | - 内部で利用するblogsyncのバージョンをv0.20.1にアップデート by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/61 99 | 100 | ## [v1.2.11](https://github.com/hatena/hatenablog-workflows/compare/v1.2.10...v1.2.11) - 2023-12-26 101 | - upload-imageのoutputsにrevisionを追加 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/56 102 | 103 | ## [v1.2.10](https://github.com/hatena/hatenablog-workflows/compare/v1.2.9...v1.2.10) - 2023-12-25 104 | - upload-imagesアクションを追加 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/49 105 | - chore(deps): update tj-actions/changed-files action to v41 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/54 106 | 107 | ## [v1.2.9](https://github.com/hatena/hatenablog-workflows/compare/v1.2.8...v1.2.9) - 2023-12-20 108 | - create-draft時のタイトルの埋め込みでタイトルをYAMLの値としてエスケープする by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/52 109 | 110 | ## [v1.2.8](https://github.com/hatena/hatenablog-workflows/compare/v1.2.7...v1.2.8) - 2023-12-07 111 | - delete other filesの処理を修正 by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/47 112 | 113 | ## [v1.2.7](https://github.com/hatena/hatenablog-workflows/compare/v1.2.6...v1.2.7) - 2023-12-05 114 | - git管理下にあるファイルはrmではなくgit restoreする by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/45 115 | 116 | ## [v1.2.6](https://github.com/hatena/hatenablog-workflows/compare/v1.2.5...v1.2.6) - 2023-11-30 117 | - ${{ inputs.title }}を環境変数経由で$ENTRY_TITLEとして渡す by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/43 118 | 119 | ## [v1.2.5](https://github.com/hatena/hatenablog-workflows/compare/v1.2.4...v1.2.5) - 2023-11-29 120 | - README の typo を修正 by @astj in https://github.com/hatena/hatenablog-workflows/pull/40 121 | - 空白を含むタイトルであっても pull draft を実行可能とする by @hsbt in https://github.com/hatena/hatenablog-workflows/pull/42 122 | 123 | ## [v1.2.4](https://github.com/hatena/hatenablog-workflows/compare/v1.2.3...v1.2.4) - 2023-11-09 124 | - blogsyncのバージョンをv0.18.2にする by @theoremoon in https://github.com/hatena/hatenablog-workflows/pull/38 125 | 126 | ## [v1.2.3](https://github.com/hatena/hatenablog-workflows/compare/v1.2.2...v1.2.3) - 2023-11-02 127 | - BLOG_DOMAINの改行コード対応 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/36 128 | 129 | ## [v1.2.2](https://github.com/hatena/hatenablog-workflows/compare/v1.2.1...v1.2.2) - 2023-10-31 130 | - タイトルからファイルを特定する処理の不備を修正 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/34 131 | 132 | ## [v1.2.1](https://github.com/hatena/hatenablog-workflows/compare/v1.2.0...v1.2.1) - 2023-10-27 133 | - tagprを導入 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/25 134 | - プレビュー用URLを下書きPRのbodyに追加する by @halkt in https://github.com/hatena/hatenablog-workflows/pull/32 135 | 136 | ## [v1.2.0](https://github.com/hatena/hatenablog-workflows/compare/v1.1.3...v1.2.0) - 2023-10-26 137 | - fix typo by @Songmu in https://github.com/hatena/hatenablog-workflows/pull/26 138 | - blogsyncのバージョンのdefaultで最新のものをみるように修正&呼び出し時に指定できるようにする by @halkt in https://github.com/hatena/hatenablog-workflows/pull/29 139 | - chore(deps): update tj-actions/changed-files action to v40 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/30 140 | 141 | ## [v1.1.3](https://github.com/hatena/hatenablog-workflows/compare/v1.1.2...v1.1.3) - 2023-10-16 142 | - fix: 下書き記事が存在しない際にエラーになる現象を修正 by @spider-man-tm in https://github.com/hatena/hatenablog-workflows/pull/24 143 | 144 | ## [v1.1.2](https://github.com/hatena/hatenablog-workflows/compare/v1.1.1...v1.1.2) - 2023-10-12 145 | - blogsyncのバージョンをv0.13.5に固定 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/21 146 | 147 | ## [v1.1.1](https://github.com/hatena/hatenablog-workflows/compare/v1.1.0...v1.1.1) - 2023-10-02 148 | - Entry IDをURLから抽出する処理が末尾にのみマッチするよう修正 by @uta8a in https://github.com/hatena/hatenablog-workflows/pull/18 149 | 150 | ## [v1.1.0](https://github.com/hatena/hatenablog-workflows/compare/v1.0.0...v1.1.0) - 2023-09-22 151 | - initialize時に下書きファイルを同期対象に含めるか選択できるようにする by @halkt in https://github.com/hatena/hatenablog-workflows/pull/17 152 | 153 | ## [v1.2.1](https://github.com/hatena/hatenablog-workflows/compare/v1.2.1...v1) - 2023-10-31 154 | - タイトルからファイルを特定する処理の不備を修正 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/34 155 | 156 | ## [v1.0.0](https://github.com/hatena/hatenablog-workflows/commits/v1.0.0) - 2023-09-21 157 | - Configure Renovate by @renovate in https://github.com/hatena/hatenablog-workflows/pull/1 158 | - update README.md by @halkt in https://github.com/hatena/hatenablog-workflows/pull/3 159 | - Update actions/checkout action to v4 by @renovate in https://github.com/hatena/hatenablog-workflows/pull/4 160 | - README.md 更新 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/5 161 | - README.md 修正 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/6 162 | - composit actionのrefをv1に指定 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/8 163 | - Reusable workflows の名称を変更 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/9 164 | - git restore-mtime をsetupで実行する by @halkt in https://github.com/hatena/hatenablog-workflows/pull/10 165 | - owner の定義がyamlにない場合はusernameをセットする by @halkt in https://github.com/hatena/hatenablog-workflows/pull/11 166 | - owner name取得処理を分割 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/12 167 | - 特定のブランチの場合は処理を実行しない by @halkt in https://github.com/hatena/hatenablog-workflows/pull/13 168 | - 処理のskipでlabelを利用する by @halkt in https://github.com/hatena/hatenablog-workflows/pull/14 169 | - draft templateのファイル名修正 by @halkt in https://github.com/hatena/hatenablog-workflows/pull/15 170 | --------------------------------------------------------------------------------