From 7b57732b712d8145320332c4fb42cdfddb1aa6a5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 22 Jul 2023 14:06:40 +0200 Subject: [PATCH 1/7] ci: update the books via a GitHub workflow With this commit, the SHAs corresponding to the various repositories containing the ProGit Book and its translations are stored in a sparse-checkout'able directory. This information is then used by a scheduled workflow to determine what needs to be updated (if anything) and then performing that task. Signed-off-by: Johannes Schindelin --- .../actions/deploy-to-github-pages/action.yml | 57 ++++++++ .github/workflows/update-book.yml | 136 ++++++++++++++++++ _sync_state/.gitignore | 0 script/ci-helper.js | 65 +++++++++ 4 files changed, 258 insertions(+) create mode 100644 .github/actions/deploy-to-github-pages/action.yml create mode 100644 .github/workflows/update-book.yml create mode 100644 _sync_state/.gitignore create mode 100644 script/ci-helper.js diff --git a/.github/actions/deploy-to-github-pages/action.yml b/.github/actions/deploy-to-github-pages/action.yml new file mode 100644 index 0000000000..ef5aca3fa7 --- /dev/null +++ b/.github/actions/deploy-to-github-pages/action.yml @@ -0,0 +1,57 @@ +name: 'Run Hugo/Pagefind and deploy to GitHub Pages' +description: 'Runs Hugo and Pagefind and then deploys the result to GitHub Pages.' +# This composite Action requires the following things in the calling workflow: +# +# permissions: +# contents: write # to push changes (if any) +# pages: write # to deploy to GitHub Pages +# id-token: write # to verify that the deployment source is legit +# environment: +# name: github-pages +# url: ${{ steps..outputs.url }} +outputs: + url: + description: The URL to which the site was deployed + value: ${{ steps.deploy.outputs.page_url }} +runs: + using: "composite" + steps: + - name: push changes + shell: bash + run: git push origin HEAD + + - name: un-sparse worktree to prepare for deployment + shell: bash + run: git sparse-checkout disable + + - name: setup GitHub Pages + id: pages + uses: actions/configure-pages@v3 + + - name: install Hugo + env: + HUGO_VERSION: 0.120.3 + shell: bash + run: | + set -x && + curl -Lo /tmp/hugo.deb https://github.com/gohugoio/hugo/releases/download/v$HUGO_VERSION/hugo_extended_${HUGO_VERSION}_linux-amd64.deb && + sudo dpkg -i /tmp/hugo.deb + + - name: run Hugo to build the pages + env: + HUGO_RELATIVEURLS: false + shell: bash + run: hugo --minify --baseURL "${{ steps.pages.outputs.base_url }}/" + + - name: run Pagefind to build the search index + shell: bash + run: npx -y pagefind --site public + + - name: upload GitHub Pages artifact + uses: actions/upload-pages-artifact@v2 + with: + path: ./public + + - name: deploy + id: deploy + uses: actions/deploy-pages@v2 diff --git a/.github/workflows/update-book.yml b/.github/workflows/update-book.yml new file mode 100644 index 0000000000..76326c1c57 --- /dev/null +++ b/.github/workflows/update-book.yml @@ -0,0 +1,136 @@ +name: Update Progit Book + +on: + workflow_dispatch: + schedule: + # check daily for updates + - cron: '29 4 * * *' + +jobs: + check-for-updates: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + sparse-checkout: | + _sync_state + script + - uses: actions/github-script@v6 + id: get-pending + with: + script: | + const { getPendingBookUpdates } = require('./script/ci-helper.js') + + const pending = await getPendingBookUpdates(github) + // an empty matrix is invalid and makes the workflow run fail, unfortunately + return pending.length ? pending : [''] + - name: ruby setup + # Technically, we do not need Ruby in this job. But we do want to cache + # Ruby & The Gems for use in the matrix in the next job. + if: steps.get-pending.outputs.result != '[""]' + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + outputs: + matrix: ${{ steps.get-pending.outputs.result }} + update-book: + needs: check-for-updates + if: needs.check-for-updates.outputs.matrix != '[""]' + runs-on: ubuntu-latest + strategy: + matrix: + language: ${{ fromJson(needs.check-for-updates.outputs.matrix) }} + fail-fast: false + steps: + - uses: actions/checkout@v3 + with: + sparse-checkout: | + _sync_state + script + data + content/book/${{ matrix.language.lang }} + static/book/${{ matrix.language.lang }} + - name: clone ${{ matrix.language.repository }} + run: | + printf '%s\n' /progit-clone/ /vendor >>.git/info/exclude && + + # Clone the book's sources + git clone --depth 1 --single-branch \ + https://github.com/${{ matrix.language.repository }} progit-clone + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: update book/${{ matrix.language.lang }} + run: | + # this seems to be needed to let `bundle exec` see `vendor/bundle/` + { bundle check || bundle install --frozen; } && + + # generate the HTML + bundle exec ruby ./script/update-book2.rb ${{ matrix.language.lang }} progit-clone + - name: commit changes + run: | + # record the commit hash + mkdir -p _sync_state && + git -C progit-clone rev-parse HEAD >_sync_state/book-${{ matrix.language.lang }}.sha && + + # commit it all + git add -A \ + _sync_state \ + data/book-${{ matrix.language.lang }}.yml \ + content/book && + # there might be images + if test -d static/book + then + git add -A static/book + fi && + git -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + commit -m 'book: update ${{ matrix.language.lang }}' \ + -m 'Updated via the `update-book.yml` GitHub workflow.' + - name: verify that there are no uncommitted changes + run: | + git update-index --refresh && + if test -n "$(git diff HEAD)$(git ls-files --exclude-standard --other)" + then + echo '::error::there are uncommitted changes!' >&2 + git status >&2 + exit 1 + fi + - name: generate the bundle + run: | + git branch -m book-${{ matrix.language.lang }} + git bundle create ${{ matrix.language.lang }}.bundle refs/remotes/origin/${{ github.ref_name }}..book-${{ matrix.language.lang }} + - uses: actions/upload-artifact@v3 + with: + name: bundle-${{ matrix.language.lang }} + path: ${{ matrix.language.lang }}.bundle + push-updates: + needs: [check-for-updates, update-book] + if: needs.check-for-updates.outputs.matrix != '[""]' + permissions: + contents: write # to push changes (if any) + pages: write # to deploy to GitHub Pages + id-token: write # to verify that the deployment source is legit + environment: + name: github-pages + url: ${{ steps.deploy.outputs.url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/download-artifact@v3 + - name: apply updates + id: apply + run: | + for lang in $(echo '${{ needs.check-for-updates.outputs.matrix }}' | + sed -n 's/\[\?{[^}]*"lang":"\([^"]*\)[^}]*},\?\]\?/\1 /gp') + do + git -c core.editor=: \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + pull --no-rebase bundle-$lang/$lang.bundle book-$lang || + exit 1 + done + - name: deploy to GitHub Pages + id: deploy + uses: ./.github/actions/deploy-to-github-pages diff --git a/_sync_state/.gitignore b/_sync_state/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/script/ci-helper.js b/script/ci-helper.js new file mode 100644 index 0000000000..e955494dbd --- /dev/null +++ b/script/ci-helper.js @@ -0,0 +1,65 @@ +const fs = require('fs') + +const getFileContents = async (path) => { + return (await fs.promises.readFile(path)).toString('utf-8').trim() +} + +const getAllBooks = async () => { + const book_rb = await getFileContents("script/book.rb"); + const begin = book_rb.indexOf('@@all_books = {') + const end = book_rb.indexOf('}', begin + 1) + if (begin < 0 || end < 0) throw new Error(`Could not find @@all_books in:\n${book_rb}`) + return book_rb + .substring(begin, end) + .split('\n') + .reduce((allBooks, line) => { + const match = line.match(/"([^"]+)" => "([^"]+)"/) + if (match) allBooks[match[1]] = match[2] + return allBooks + }, {}) +} + +const getPendingBookUpdates = async (octokit) => { + const books = await getAllBooks() + const result = [] + for (const lang of Object.keys(books)) { + try { + const localSha = await getFileContents(`_sync_state/book-${lang}.sha`) + + const [owner, repo] = books[lang].split('/') + const { data: { default_branch: remoteDefaultBranch } } = + await octokit.rest.repos.get({ + owner, + repo + }) + const { data: { object: { sha: remoteSha } } } = + await octokit.rest.git.getRef({ + owner, + repo, + ref: `heads/${remoteDefaultBranch}` + }) + + if (localSha === remoteSha) continue + } catch (e) { + // It's okay for the `.sha` file not to exist yet.` + if (e.code !== 'ENOENT') throw e + } + result.push({ + lang, + repository: books[lang] + }) + } + return result +} + +// for testing locally, needs `npm install @octokit/rest` to work +if (require.main === module) { + (async () => { + const { Octokit } = require('@octokit/rest') + console.log(await getPendingBookUpdates(new Octokit())) + })().catch(console.log) +} + +module.exports = { + getPendingBookUpdates +} \ No newline at end of file From 5874f9ad94ff96953fd3d03262c59d1105de58ef Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 8 Oct 2023 00:18:36 +0200 Subject: [PATCH 2/7] Update the download data via a GitHub workflow This workflow has a `workflow_dispatch` trigger to allow for manually starting this workflow directly after, say, a Git for Windows version was released. Signed-off-by: Johannes Schindelin --- .github/workflows/update-download-data.yml | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/update-download-data.yml diff --git a/.github/workflows/update-download-data.yml b/.github/workflows/update-download-data.yml new file mode 100644 index 0000000000..dfed1af6db --- /dev/null +++ b/.github/workflows/update-download-data.yml @@ -0,0 +1,69 @@ +name: Update download data + +on: + workflow_dispatch: + schedule: + # check daily for updates + - cron: '31 13 * * *' + +jobs: + update-download-data: + runs-on: ubuntu-latest + permissions: + contents: write # to push changes (if any) + pages: write # to deploy to GitHub Pages + id-token: write # to verify that the deployment source is legit + environment: + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: + - uses: actions/checkout@v3 + with: + sparse-checkout: | + .github/actions + _sync_state + script + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: update download data + run: | + # this seems to be needed to let `bundle exec` see `vendor/bundle/` + { bundle check || bundle install --frozen; } && + + # update download data + bundle exec ruby script/update-download-data.rb + - name: commit changes (if any) + id: commit + run: | + # Exit early if there are no changes + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules -- hugo.yml && + exit 0 + + what="$(git diff hugo.yml | + sed -n '/^+ *filename: Git-\([.0-9]*\)-.*\.exe$/{s/.* Git-\([.0-9]*\)-.*/Git for Windows v\1/p;q}')" + mac="$(git diff hugo.yml | + sed -n 's/^+ *filename: git-\([.0-9]*\)-.*\.dmg$/Git for macOS v\1/p')" + test -z "$mac" || what="$what${what:+, }$mac" + commit_message="Update download data${what:+ ($what)}" + echo "result=$commit_message" >>$GITHUB_OUTPUT + + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + commit -m "$commit_message" -- hugo.yml + - name: verify that there are no uncommitted changes + run: | + git update-index --refresh && + if test -n "$(git diff HEAD)$(git ls-files --exclude-standard --other)" + then + echo '::error::there are uncommitted changes!' >&2 + git status >&2 + exit 1 + fi + - name: deploy to GitHub Pages + if: steps.commit.outputs.result != '' + id: deploy + uses: ./.github/actions/deploy-to-github-pages From 5669a77c600c5edf2a76d984ca75b8d6289361fb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 11 Oct 2023 14:35:52 +0200 Subject: [PATCH 3/7] update-book: allow force-rebuilding Even when everything is marked up to date, it is sometimes necessary to rebuild the books e.g. due to style/layout changes. With this here commit, that is possible by toggling the `force-rebuild` flag of the `workflow_dispatch` trigger. This commit is best viewed with `git show -w`. Signed-off-by: Johannes Schindelin --- .github/workflows/update-book.yml | 7 +++++- script/ci-helper.js | 40 ++++++++++++++++--------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/.github/workflows/update-book.yml b/.github/workflows/update-book.yml index 76326c1c57..19758265dd 100644 --- a/.github/workflows/update-book.yml +++ b/.github/workflows/update-book.yml @@ -2,6 +2,11 @@ name: Update Progit Book on: workflow_dispatch: + inputs: + force-rebuild: + description: Force re-building all books (e.g. after a script change) + type: boolean + default: false schedule: # check daily for updates - cron: '29 4 * * *' @@ -21,7 +26,7 @@ jobs: script: | const { getPendingBookUpdates } = require('./script/ci-helper.js') - const pending = await getPendingBookUpdates(github) + const pending = await getPendingBookUpdates(github, ${{ inputs.force-rebuild }}) // an empty matrix is invalid and makes the workflow run fail, unfortunately return pending.length ? pending : [''] - name: ruby setup diff --git a/script/ci-helper.js b/script/ci-helper.js index e955494dbd..8bf69e3de2 100644 --- a/script/ci-helper.js +++ b/script/ci-helper.js @@ -19,30 +19,32 @@ const getAllBooks = async () => { }, {}) } -const getPendingBookUpdates = async (octokit) => { +const getPendingBookUpdates = async (octokit, forceRebuild) => { const books = await getAllBooks() const result = [] for (const lang of Object.keys(books)) { - try { - const localSha = await getFileContents(`_sync_state/book-${lang}.sha`) + if (!forceRebuild) { + try { + const localSha = await getFileContents(`_sync_state/book-${lang}.sha`) - const [owner, repo] = books[lang].split('/') - const { data: { default_branch: remoteDefaultBranch } } = - await octokit.rest.repos.get({ - owner, - repo - }) - const { data: { object: { sha: remoteSha } } } = - await octokit.rest.git.getRef({ - owner, - repo, - ref: `heads/${remoteDefaultBranch}` - }) + const [owner, repo] = books[lang].split('/') + const { data: { default_branch: remoteDefaultBranch } } = + await octokit.rest.repos.get({ + owner, + repo + }) + const { data: { object: { sha: remoteSha } } } = + await octokit.rest.git.getRef({ + owner, + repo, + ref: `heads/${remoteDefaultBranch}` + }) - if (localSha === remoteSha) continue - } catch (e) { - // It's okay for the `.sha` file not to exist yet.` - if (e.code !== 'ENOENT') throw e + if (localSha === remoteSha) continue + } catch (e) { + // It's okay for the `.sha` file not to exist yet.` + if (e.code !== 'ENOENT') throw e + } } result.push({ lang, From 5a53855f4610dbcec22bfd4f508b825b519a1a62 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Oct 2023 00:03:32 +0200 Subject: [PATCH 4/7] Add a workflow to update the Git version and manual pages Whenever a new Git version is released, we will want git-scm.com to be updated automatically. To this end, add a scheduled workflow that does precisely that. In case someone gets too impatient to wait for the daily check, there's also a `workflow_dispatch` trigger. Note: It would be tempting to use a partial clone of git.git for this, but that would cause many, many round-trips (each time a blob is requested), especially when force-rebuilding all versions of all manual pages (which will be added in a later commit). So let's not. Signed-off-by: Johannes Schindelin --- .../update-git-version-and-manual-pages.yml | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/update-git-version-and-manual-pages.yml diff --git a/.github/workflows/update-git-version-and-manual-pages.yml b/.github/workflows/update-git-version-and-manual-pages.yml new file mode 100644 index 0000000000..9e912bcf08 --- /dev/null +++ b/.github/workflows/update-git-version-and-manual-pages.yml @@ -0,0 +1,83 @@ +name: Synchronize with new Git version (if any) + +on: + workflow_dispatch: + schedule: + # check daily for updates + - cron: '37 17 * * *' + +jobs: + update-git-version-and-manual-pages: + runs-on: ubuntu-latest + permissions: + contents: write # to push changes (if any) + pages: write # to deploy to GitHub Pages + id-token: write # to verify that the deployment source is legit + environment: + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: + - uses: actions/checkout@v3 + with: + sparse-checkout: | + .github/actions + _sync_state + script + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: update recorded Git version + env: + GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # this seems to be needed to let `bundle exec` see `vendor/bundle/` + { bundle check || bundle install --frozen; } && + + # update recorded Git version + bundle exec ruby script/update-git-version.rb + - name: commit changes (if any) + id: commit + run: | + # Exit early if there are no changes + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules -- hugo.yml && + exit 0 + + version="$(git diff hugo.yml | sed -n '/^+ *latest_version: /{s/.*: //;p;q}')" + echo "result=$version" >>$GITHUB_OUTPUT + + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + commit -m "Update git-version ($version)" -- hugo.yml + - name: prepare worktree + if: steps.commit.outputs.result != '' + run: git sparse-checkout disable + - name: clone git.git + if: steps.commit.outputs.result != '' + run: git clone --bare https://github.com/git/git '${{ runner.temp }}/git' + - name: update manual pages + if: steps.commit.outputs.result != '' + run: bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en + - name: commit manual pages + if: steps.commit.outputs.result != '' + run: | + git add -A _generated-asciidoc data/docs.yml content/docs && + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + commit -m "Update manual pages (${{ steps.commit.outputs.result }})" + - name: verify that there are no uncommitted changes + run: | + git update-index --refresh && + if test -n "$(git diff HEAD)$(git ls-files --exclude-standard --other)" + then + echo '::error::there are uncommitted changes!' >&2 + git status >&2 + exit 1 + fi + - name: deploy to GitHub Pages + if: steps.commit.outputs.result != '' + id: deploy + uses: ./.github/actions/deploy-to-github-pages From 07d70626027e2b5728b9a2656c207f54bb7e72ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Oct 2023 08:00:55 +0200 Subject: [PATCH 5/7] update-manual-pages: optionally force a complete rebuild Sometimes a bug is found in the way the manual pages are built, requiring a change e.g. in the `update-docs.rb` script or in the corresponding style sheets. In such a case, the manual pages have to be rebuilt from scratch, even if they exist already. This commit introduces the `force-rebuild` flag to do this. Signed-off-by: Johannes Schindelin --- .../update-git-version-and-manual-pages.yml | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/update-git-version-and-manual-pages.yml b/.github/workflows/update-git-version-and-manual-pages.yml index 9e912bcf08..34b10cdb2f 100644 --- a/.github/workflows/update-git-version-and-manual-pages.yml +++ b/.github/workflows/update-git-version-and-manual-pages.yml @@ -2,6 +2,11 @@ name: Synchronize with new Git version (if any) on: workflow_dispatch: + inputs: + force-rebuild: + description: Force re-building all manual pages (e.g. after a script change) + type: boolean + default: false schedule: # check daily for updates - cron: '37 17 * * *' @@ -52,22 +57,32 @@ jobs: -c user.email=${{ github.actor }}@noreply.github.com \ commit -m "Update git-version ($version)" -- hugo.yml - name: prepare worktree - if: steps.commit.outputs.result != '' + if: steps.commit.outputs.result != '' || inputs.force-rebuild run: git sparse-checkout disable - name: clone git.git - if: steps.commit.outputs.result != '' + if: steps.commit.outputs.result != '' || inputs.force-rebuild run: git clone --bare https://github.com/git/git '${{ runner.temp }}/git' - name: update manual pages - if: steps.commit.outputs.result != '' - run: bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en + if: steps.commit.outputs.result != '' || inputs.force-rebuild + run: | + test false = '${{ inputs.force-rebuild }}' || export RERUN=true + bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git' en - name: commit manual pages - if: steps.commit.outputs.result != '' + id: manual-pages + if: steps.commit.outputs.result != '' || inputs.force-rebuild run: | git add -A _generated-asciidoc data/docs.yml content/docs && + if test false != '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD -- + then + echo '::notice::A manual pages rebuild was requested but resulted in no changes' >&2 + exit 0 + fi && + version='${{ steps.commit.outputs.result }}' && git \ -c user.name=${{ github.actor }} \ -c user.email=${{ github.actor }}@noreply.github.com \ - commit -m "Update manual pages (${{ steps.commit.outputs.result }})" + commit -m "Update manual pages (${version:-manually forced rebuild})" && + echo "result=modified" >>$GITHUB_OUTPUT - name: verify that there are no uncommitted changes run: | git update-index --refresh && @@ -78,6 +93,6 @@ jobs: exit 1 fi - name: deploy to GitHub Pages - if: steps.commit.outputs.result != '' + if: steps.commit.outputs.result != '' || steps.manual-pages.outputs.result != '' id: deploy uses: ./.github/actions/deploy-to-github-pages From 7b4af49741b925bab02041018862506ac7aba3e7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 10 Oct 2023 22:20:50 +0200 Subject: [PATCH 6/7] Add a GitHub workflow to deploy `gh-pages` to GitHub Pages When merging PRs, or pushing changes directly, we will want to deploy the result to GitHub Pages. That's this new workflow's job. Signed-off-by: Johannes Schindelin --- .../actions/deploy-to-github-pages/action.yml | 6 +++-- .github/workflows/deploy.yml | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/actions/deploy-to-github-pages/action.yml b/.github/actions/deploy-to-github-pages/action.yml index ef5aca3fa7..6c3df49649 100644 --- a/.github/actions/deploy-to-github-pages/action.yml +++ b/.github/actions/deploy-to-github-pages/action.yml @@ -16,9 +16,11 @@ outputs: runs: using: "composite" steps: - - name: push changes + - name: push changes (if needed) shell: bash - run: git push origin HEAD + run: | + test "$(git rev-parse HEAD)" = "$(git rev-parse refs/remotes/origin/${{ github.ref_name }})" || + git push origin HEAD:${{ github.ref }} - name: un-sparse worktree to prepare for deployment shell: bash diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000..c73f1fe639 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,22 @@ +name: Deploy to GitHub Pages + +on: + workflow_dispatch: + push: + branches: + - gh-pages + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + pages: write # to deploy to GitHub Pages + id-token: write # to verify that the deployment source is legit + environment: + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: + - uses: actions/checkout@v3 + - name: deploy to GitHub Pages + id: deploy + uses: ./.github/actions/deploy-to-github-pages From 53408216a0a7418fb9e844e58192ebb63ad557ca Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 11 Oct 2023 13:32:47 +0200 Subject: [PATCH 7/7] Add a GitHub workflow to update the translated manual pages regularly After taking care of the English manual pages, let's also have a scheduled workflow that takes care of updating the translated manual pages. Just like the scheduled workflow that takes care of the original, English manual pages, here we also need a flag to toggle complete rebuilds in case the script, the style sheets, or the layouts changed. Signed-off-by: Johannes Schindelin --- .../update-translated-manual-pages.yml | 84 +++++++++++++++++++ script/ci-helper.js | 30 ++++++- 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/update-translated-manual-pages.yml diff --git a/.github/workflows/update-translated-manual-pages.yml b/.github/workflows/update-translated-manual-pages.yml new file mode 100644 index 0000000000..d36d13a186 --- /dev/null +++ b/.github/workflows/update-translated-manual-pages.yml @@ -0,0 +1,84 @@ +name: Update translated manual pages + +on: + workflow_dispatch: + inputs: + force-rebuild: + description: Force re-building all manual pages (e.g. after a script change) + type: boolean + default: false + schedule: + # check daily for updates + - cron: '41 19 * * *' + +jobs: + check-for-updates: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + sparse-checkout: | + _sync_state + script + - uses: actions/github-script@v6 + id: get-pending + with: + script: | + const { areTranslatedManualPagesUpToDate } = require('./script/ci-helper.js') + + return await areTranslatedManualPagesUpToDate(github) + outputs: + up-to-date: ${{ steps.get-pending.outputs.result }} + update-translated-manual-pages: + needs: [check-for-updates] + if: inputs.force-rebuild || needs.check-for-updates.outputs.up-to-date == 'false' + runs-on: ubuntu-latest + permissions: + contents: write # to push changes (if any) + pages: write # to deploy to GitHub Pages + id-token: write # to verify that the deployment source is legit + environment: + name: github-pages + url: ${{ steps.deploy.outputs.url }} + steps: + - uses: actions/checkout@v3 + - name: ruby setup + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - name: clone jnavila/git-html-l10n + run: git clone --bare https://github.com/jnavila/git-html-l10n '${{ runner.temp }}/git-html-l10n' + - name: update translated manual pages + run: | + test false = '${{ inputs.force-rebuild }}' || export RERUN=true + bundle exec ruby script/update-docs.rb '${{ runner.temp }}/git-html-l10n' l10n + - name: commit translated manual pages + id: manual-pages + run: | + git -C '${{ runner.temp }}/git-html-l10n' rev-parse HEAD >_sync_state/git-html-l10n.sha && + git add _sync_state/git-html-l10n.sha && + + git add -A _generated-asciidoc/ data/docs.yml content/docs && + if test false != '${{ inputs.force-rebuild }}' && git diff-index --cached --quiet HEAD -- + then + echo '::notice::Rebuild of the translated manual pages was requested but resulted in no changes' >&2 + exit 0 + fi && + git \ + -c user.name=${{ github.actor }} \ + -c user.email=${{ github.actor }}@noreply.github.com \ + commit -m "Update translated manual pages" && + echo "result=modified" >>$GITHUB_OUTPUT + - name: verify that there are no uncommitted changes + run: | + git update-index --refresh && + if test -n "$(git diff HEAD)$(git ls-files --exclude-standard --other)" + then + echo '::error::there are uncommitted changes!' >&2 + git status >&2 + exit 1 + fi + - name: deploy to GitHub Pages + if: steps.manual-pages.outputs.result != '' + id: deploy + uses: ./.github/actions/deploy-to-github-pages diff --git a/script/ci-helper.js b/script/ci-helper.js index 8bf69e3de2..1914c87678 100644 --- a/script/ci-helper.js +++ b/script/ci-helper.js @@ -54,14 +54,40 @@ const getPendingBookUpdates = async (octokit, forceRebuild) => { return result } +const areTranslatedManualPagesUpToDate = async (octokit) => { + try { + const localSha = await getFileContents(`_sync_state/git-html-l10n.sha`) + + const [owner, repo] = 'jnavila/git-html-l10n'.split('/') + const { data: { default_branch: remoteDefaultBranch } } = + await octokit.rest.repos.get({ + owner, + repo + }) + const { data: { object: { sha: remoteSha } } } = + await octokit.rest.git.getRef({ + owner, + repo, + ref: `heads/${remoteDefaultBranch}` + }) + + if (localSha === remoteSha) return true + } catch (e) { + // It's okay for the `.sha` file not to exist yet.` + if (e.code !== 'ENOENT') throw e + } + return false +} + // for testing locally, needs `npm install @octokit/rest` to work if (require.main === module) { (async () => { const { Octokit } = require('@octokit/rest') - console.log(await getPendingBookUpdates(new Octokit())) + console.log(await areTranslatedManualPagesUpToDate(new Octokit())) })().catch(console.log) } module.exports = { - getPendingBookUpdates + getPendingBookUpdates, + areTranslatedManualPagesUpToDate } \ No newline at end of file