diff --git a/.eslintrc.json b/.eslintrc.json index b82e79e129..80379384b6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -235,7 +235,13 @@ "no-unsanitized/property": "error", "jsdoc/check-access": "error", "jsdoc/check-alignment": "error", - "jsdoc/check-line-alignment": "error", + "jsdoc/check-line-alignment": [ + "error", + "never", + { + "wrapIndent": " " + } + ], "jsdoc/check-param-names": "error", "jsdoc/check-property-names": "error", "jsdoc/check-tag-names": "error", @@ -244,10 +250,6 @@ "jsdoc/empty-tags": "error", "jsdoc/implements-on-classes": "error", "jsdoc/multiline-blocks": "error", - "jsdoc/newline-after-description": [ - "error", - "never" - ], "jsdoc/no-bad-blocks": "error", "jsdoc/no-multi-asterisks": "error", "jsdoc/require-asterisk-prefix": "error", diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0bbc7cb852..cbd48cc88f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,13 +3,27 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" labels: - "area/dependencies" + groups: # group minor/patch updates together + minor: + patterns: + - "*" + update-types: + - "minor" + - "patch" - package-ecosystem: "npm" directory: "/" schedule: - interval: "weekly" + interval: "monthly" labels: - "area/dependencies" + groups: # group minor/patch updates together + minor: + patterns: + - "*" + update-types: + - "minor" + - "patch" diff --git a/.github/workflows/auto-approve-run.yml b/.github/workflows/auto-approve-run.yml index 4c6c26ba9a..bac4c76b51 100644 --- a/.github/workflows/auto-approve-run.yml +++ b/.github/workflows/auto-approve-run.yml @@ -13,7 +13,7 @@ jobs: if: github.actor == 'tatsumoto-ren' steps: - name: Download workflow artifact - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 + uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # v2.28.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} workflow: auto-approve.yml diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index 250903fcc0..7e67a92b5f 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -11,7 +11,7 @@ jobs: PR_NUM: ${{ github.event.number }} run: echo $PR_NUM > pr_num.txt - name: Upload the PR number - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: pr_num path: ./pr_num.txt \ No newline at end of file diff --git a/.github/workflows/broken-links.yml b/.github/workflows/broken-links.yml index bb2028c644..45043e7960 100644 --- a/.github/workflows/broken-links.yml +++ b/.github/workflows/broken-links.yml @@ -16,7 +16,7 @@ jobs: link-checker: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: lycheeverse/lychee-action@ec3ed119d4f44ad2673a7232460dc7dff59d2421 with: fail: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c62c9893af..4a767541d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v3 @@ -41,16 +41,11 @@ jobs: env: CI: true - - name: Manifest - run: npm run test-manifest - env: - CI: true + - name: Build + run: npm run build - name: Validate manifest.json of the extension uses: cardinalby/schema-validator-action@c2da05377e89dd0c9b7be9420da0b3534b1efcce # pin@v1 with: file: ext/manifest.json schema: "https://json.schemastore.org/chrome-manifest.json" - - - name: Build - run: npm run test-build diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a06541ca3d..bac2d4b850 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/create-prerelease-on-tag.yml b/.github/workflows/create-prerelease-on-tag.yml index 06cb8c7e60..075b7a0210 100644 --- a/.github/workflows/create-prerelease-on-tag.yml +++ b/.github/workflows/create-prerelease-on-tag.yml @@ -7,13 +7,15 @@ on: permissions: contents: read jobs: - build-release-publish: + build: runs-on: ubuntu-latest permissions: actions: write contents: write + outputs: + hashes: ${{ steps.hash.outputs.hashes }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v3 @@ -21,9 +23,15 @@ jobs: node-version-file: ".node-version" - name: Lint - run: npm run-script build + run: npm run-script build -- --all --rikaitan-version ${{ github.ref_name }} shell: bash + - name: Generate hashes + id: hash + run: | + cd builds + echo "hashes=$(sha256sum * | base64 -w0)" >> "$GITHUB_OUTPUT" + - name: Release id: release uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # pin@v0.1.15 @@ -46,3 +54,14 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} wait-for-completion: false inputs: '{ "upload_url": "${{ steps.release.outputs.upload_url }}" }' + + provenance: + needs: [build] + permissions: + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + contents: write # To add assets to a release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0 + with: + base64-subjects: "${{ needs.build.outputs.hashes }}" + upload-assets: true diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index eddc4b9a7a..db623c0aa0 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,14 +13,14 @@ jobs: - name: Remove all fonts run: rm -rf /usr/share/fonts - - uses: actions/checkout@v3 - + - uses: actions/checkout@v4 + - name: Install CJK fonts uses: awalsh128/cache-apt-pkgs-action@1850ee53f6e706525805321a3f2f863dcf73c962 # v1.3.0 with: packages: fonts-ipafont-mincho execute_install_scripts: true - + - uses: actions/setup-node@v3 with: cache: "npm" @@ -29,6 +29,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Build + run: npm run build + - name: Cache playwright browsers id: cache-playwright uses: actions/cache@v3 @@ -40,16 +43,16 @@ jobs: - if: ${{ steps.cache-playwright.outputs.cache-hit != 'true' }} name: Install Playwright Browsers run: npx playwright install chromium - + - name: Grab latest dictionaries from dictionaries branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: Ajatt-Tools/rikaitan # so that this works on forks ref: dictionaries path: dictionaries - name: Grab latest screenshots from master branch - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # pin@v2 + uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # pin@v2 continue-on-error: true id: master-screenshots with: diff --git a/.github/workflows/playwright_comment.yml b/.github/workflows/playwright_comment.yml index d5a6727b30..60b3c69365 100644 --- a/.github/workflows/playwright_comment.yml +++ b/.github/workflows/playwright_comment.yml @@ -16,7 +16,7 @@ jobs: github.event.workflow_run.conclusion == 'success' steps: - name: Grab playwright-output from PR run - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # pin@v2 + uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # pin@v2 continue-on-error: true with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -24,7 +24,7 @@ jobs: name: playwright-output - name: Grab master-screenshots-outcome from PR run - uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # pin@v2 + uses: dawidd6/action-download-artifact@268677152d06ba59fcec7a7f0b5d961b6ccd7e1e # pin@v2 continue-on-error: true with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-firefox-development.yml b/.github/workflows/publish-firefox-development.yml index 2e755128d1..7701089e17 100644 --- a/.github/workflows/publish-firefox-development.yml +++ b/.github/workflows/publish-firefox-development.yml @@ -12,11 +12,13 @@ on: permissions: contents: read jobs: - build-signed-xpi-asset: + build: runs-on: ubuntu-latest environment: cd permissions: contents: write + outputs: + hashes: ${{ steps.hash.outputs.hashes }} steps: - uses: robinraju/release-downloader@efa4cd07bd0195e6cc65e9e30c251b49ce4d3e51 # pin@v1.8 with: @@ -41,6 +43,11 @@ jobs: steps.ffSignXpi.outputs.sameVersionAlreadyUploadedError != 'true' run: exit 1 + - name: Generate hashes + id: hash + run: | + echo "hashes=$(sha256sum rikaitan-firefox-dev.xpi | base64 -w0)" >> "$GITHUB_OUTPUT" + - name: Upload offline xpi release asset id: uploadReleaseAsset if: steps.ffSignXpi.outcome == 'success' @@ -54,15 +61,15 @@ jobs: asset_name: rikaitan-firefox-dev.xpi asset_content_type: application/x-xpinstall - # update update.json so that all people who have the dev version installed get the new update + # update updates.json so that all people who have the dev version installed get the new update - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: metadata - - name: Recreate update.json + - name: Recreate updates.json run: | - cat > update.json << EOF + cat > updates.json << EOF { "addons": { "{2d13e145-294e-4ead-9bce-b4644b203a00}": { @@ -84,6 +91,17 @@ jobs: git commit -a -m "${{ github.event.release.name }} - ${{ github.event.release.html_url }}" - name: Push changes - uses: ad-m/github-push-action@9a2e3c14aaecf56d5816dc3a54514f82050820b2 # pin@master + uses: ad-m/github-push-action@29f05e01bb17e6f28228b47437e03a7b69e1f9ef # pin@master with: branch: metadata + + provenance: + needs: [build] + permissions: + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + contents: write # To add assets to a release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0 + with: + base64-subjects: "${{ needs.build.outputs.hashes }}" + upload-assets: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 77d9f7adbb..1a19174038 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,7 +29,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false @@ -56,7 +56,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + uses: actions/upload-artifact@v3 with: name: SARIF file path: results.sarif diff --git a/.gitignore b/.gitignore index c6313c37b5..d13b30d032 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ dictionaries/ /test/playwright/__screenshots__/ .idea/ .vscode/ +ext/manifest.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c81b784329..ccf1c3fae3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,9 +50,10 @@ Several command line arguments are available for these scripts: * `[target]` - Builds a specific target. * `--all` - Builds all targets specified in [manifest-variants.json](dev/data/manifest-variants.json). * `--default` - Restores the default manifest file. -* `--manifest ` - Overwrites [ext/manifest.json](ext/manifest.json) with the manifest variant for the specified build target. +* `--manifest ` - Overwrites `ext/manifest.json` with the manifest variant for the specified build target. * `--dry-run` - Runs the full build process (excluding zip building), checking that the configuration is valid. * `--dry-run-build-zip` - If `--dry-run` is also specified, zip building will also be performed in memory; no files are created. +* `--rikaitan-version ` - Sets the version number in the extension manifest. Defaults to 0.0.0.0 if not set. If no arguments are specified, the command is equivalent to `build.bat --all`. @@ -66,11 +67,8 @@ Otherwise, the [JSZip](https://stuk.github.io/jszip/) API is used to generate th ## Manifest Manifest variants for different build targets are specified in [manifest-variants.json](dev/data/manifest-variants.json). -This file is used to overwrite the [manfiest.json](ext/manifest.json) file included in the extension. -By default, this manifest should be the default `chrome` manifest, and changes to [manfiest.json](ext/manifest.json) should not be committed -unless there is a corresponding change in [manifest-variants.json](dev/data/manifest-variants.json). -There is a continuous integration test which validates this, and the default manifest can be restored by running -`build.bat --default`. +This file is used to generate the `ext/manifest.json` file included in the extension. +The generated `ext/manfiest.json` should not be committed. ## Style diff --git a/README.md b/README.md index 3350425ad9..8a8a3981bd 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,10 @@ Rikaitan provides advanced features not available in other browser-based diction ## Table of Contents * [Installation](#installation) +* [Migrating from Yomichan](#migrating-from-yomichan) * [Dictionaries](#dictionaries) * [Basic Usage](#basic-usage) + * [Importing Dictionaries](#importing-dictionaries) * [Custom Dictionaries](#custom-dictionaries) * [Anki Integration](#anki-integration) * [Flashcard Configuration](#flashcard-configuration) @@ -63,6 +65,14 @@ New changes are initially introduced into the *testing* version, and after some relatively bug free, they will be promoted to the *stable* version. If you are technically savvy and don't mind submitting issues on GitHub, try the *testing* version; otherwise, the *stable* version will be your best bet. +## Migrating from Yomichan + +If you are an existing user of Yomichan, you can export your dictionary collection and settings such that they can be imported into Rikaitan to reflect your setup exactly as it was. + +You can export your settings from Yomichan's Settings page. Go to the `Backup` section and click on `Export Settings`. + +You can then import the exported files into Rikaitan from the `Backup` section of the `Settings` page. Please see [the section on importing dictionaries](#importing-dictionaries) further below for more explicit steps. + ## Dictionaries There are several free Japanese dictionaries available for Rikaitan, with two of them having glossaries available in @@ -108,6 +118,32 @@ language is not English, you may consider also importing the English version for +### Importing Dictionaries + +You can import individual dictionaries from the settings page as described above. + +Rikaitan also supports exporting and importing your entire collection of dictionaries. + +#### Importing a Dictionary Collection + +- Go to Rikaitan's Settings page (Click on the extension's icon then click on the cog icon from the popup) +- Click `Import Dictionary Collection` and select the database file you want to import +- Wait for the import to finish then turn all the dictionaries back on from the `Dictionaries > Configure installed and enabled dictionaries` section +- Refresh the browser tab to see the dictionaries in effect + +#### Exporting the Dictionary Collection + +- Click `Export Dictionary Collection` from the backup section of Rikaitan's settings page +- It will show you a progress report as it exports the data then initiates a + download for a file named something like `rikaitan-dictionaries-YYYY-MM-DD-HH-mm-ss.json` + (e.g. `rikaitan-dictionaries-2023-07-05-02-42-04.json`) + +### Importing and Exporting Personal Configuration + +Note that you can also similarly export and import your Rikaitan settings from the `Backup` section of the Settings page. + +You should be able to replicate your exact Rikaitan setup across devices by exporting your settings and dictionary collection from the source device then importing those from the destination. + ## Custom Dictionaries Rikaitan supports the use of custom dictionaries, including the esoteric but popular @@ -331,11 +367,11 @@ Yomichan is the previous name of this add-on. The versions below were created before the original developer quit. * **Google Chrome** ([stable](https://chrome.google.com/webstore/detail/yomichan/ogmnaimimemjmbakcfefmnahgdfhfami) or [testing](https://chrome.google.com/webstore/detail/yomichan-testing/bcknnfebhefllbjhagijobjklocakpdm)) - + [![image](img/chrome-web-store.png)](https://chrome.google.com/webstore/detail/yomichan/ogmnaimimemjmbakcfefmnahgdfhfami) * **Mozilla Firefox** ([stable](https://addons.mozilla.org/en-US/firefox/addon/yomichan/) or [testing](https://github.com/FooSoft/yomichan/releases)*) - + *Unlike Chrome, Firefox does not allow extensions meant for testing to be hosted in the marketplace. You will have to download a desired version and side-load it yourself. You only need to do this once and will get updates automatically. diff --git a/build.sh b/build.sh index 113862ca79..1df1997903 100755 --- a/build.sh +++ b/build.sh @@ -2,5 +2,4 @@ rm -v -rf -- ./builds/* mkdir -p -- ./builds -./bump_ver -npm run-script build -- "$@" +npm run-script build -- --all --rikaitan-version "$(./bump_ver)" "$@" diff --git a/bump_ver b/bump_ver index e247d94e3e..8e89f1284b 100755 --- a/bump_ver +++ b/bump_ver @@ -5,10 +5,11 @@ import datetime import functools import json import os.path +from typing import Optional MANIFEST_VARIANTS = os.path.join( os.path.abspath(os.path.dirname(__file__)), - 'dev', 'data', 'manifest-variants.json', + 'ext', 'manifest.json', ) @@ -52,23 +53,26 @@ def make_new_version() -> Version: ) -def calc_new_version(old_version: Version) -> Version: +def increase_ver_num(old_version: Version) -> Version: new_version = make_new_version() if old_version == new_version: new_version = old_version.bump_rev() return new_version -def main(): - with open(MANIFEST_VARIANTS, encoding='utf8') as f: - data = json.load(f) - - data['manifest']['version'] = calc_new_version(Version.from_str(data['manifest']['version'])).as_str() +def find_old_version() -> Optional[Version]: + try: + with open(MANIFEST_VARIANTS, encoding='utf8') as f: + data = json.load(f) + return Version.from_str(data['version']) + except (FileNotFoundError, TypeError, KeyError, json.JSONDecodeError): + return None - print("New version", data['manifest']['version']) - with open(MANIFEST_VARIANTS, 'w', encoding='utf8') as of: - json.dump(data, of, indent=4) +def main(): + old_version = find_old_version() + new_version = increase_ver_num(old_version) if old_version else make_new_version() + print(new_version.as_str()) if __name__ == '__main__': diff --git a/dev/build.js b/dev/build.js index e8ddc1cc0d..c087c8d0ba 100644 --- a/dev/build.js +++ b/dev/build.js @@ -108,7 +108,7 @@ function getIndexOfFilePath(array, item) { return -1; } -async function build(buildDir, extDir, manifestUtil, variantNames, manifestPath, dryRun, dryRunBuildZip) { +async function build(buildDir, extDir, manifestUtil, variantNames, manifestPath, dryRun, dryRunBuildZip, rikaitanVersion) { const sevenZipExes = ['7za', '7z']; // Create build directory @@ -130,6 +130,8 @@ async function build(buildDir, extDir, manifestUtil, variantNames, manifestPath, process.stdout.write(message); }; + process.stdout.write(`Version: ${rikaitanVersion}...\n`); + for (const variantName of variantNames) { const variant = manifestUtil.getVariant(variantName); if (typeof variant === 'undefined' || variant.buildable === false) { continue; } @@ -148,7 +150,7 @@ async function build(buildDir, extDir, manifestUtil, variantNames, manifestPath, const fileNameSafe = path.basename(fileName); const fullFileName = path.join(buildDir, fileNameSafe); if (!dryRun) { - fs.writeFileSync(manifestPath, ManifestUtil.createManifestString(modifiedManifest)); + fs.writeFileSync(manifestPath, ManifestUtil.createManifestString(modifiedManifest).replace('$RIKAITAN_VERSION', rikaitanVersion)); } if (!dryRun || dryRunBuildZip) { @@ -182,11 +184,13 @@ async function main(argv) { ['manifest', null], ['dry-run', false], ['dry-run-build-zip', false], + ['rikaitan-version', '0.0.0.0'], [null, []] ])); const dryRun = args.get('dry-run'); const dryRunBuildZip = args.get('dry-run-build-zip'); + const rikaitanVersion = args.get('rikaitan-version'); const manifestUtil = new ManifestUtil(); @@ -201,14 +205,14 @@ async function main(argv) { manifestUtil.getVariants().filter(({buildable}) => buildable !== false).map(({name}) => name) : args.get(null) ); - await build(buildDir, extDir, manifestUtil, variantNames, manifestPath, dryRun, dryRunBuildZip); + await build(buildDir, extDir, manifestUtil, variantNames, manifestPath, dryRun, dryRunBuildZip, rikaitanVersion); } finally { // Restore manifest const manifestName = (!args.get('default') && args.get('manifest') !== null) ? args.get('manifest') : null; const restoreManifest = manifestUtil.getManifest(manifestName); process.stdout.write('Restoring manifest...\n'); if (!dryRun) { - fs.writeFileSync(manifestPath, ManifestUtil.createManifestString(restoreManifest)); + fs.writeFileSync(manifestPath, ManifestUtil.createManifestString(restoreManifest).replace('$RIKAITAN_VERSION', rikaitanVersion)); } } } diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index f1eca7643f..909047e4ab 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -2,7 +2,7 @@ "manifest": { "manifest_version": 3, "name": "Rikaitan", - "version": "23.8.27.0", + "version": "$RIKAITAN_VERSION", "description": "Japanese dictionary with Anki integration", "author": "Ajatt-Tools and contributors", "icons": { @@ -357,4 +357,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/ext/action-popup.html b/ext/action-popup.html index 4e5ee478b8..23551e96d6 100644 --- a/ext/action-popup.html +++ b/ext/action-popup.html @@ -26,7 +26,7 @@