feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
这个提交包含在:
5
.github/FUNDING.yml
vendored
普通文件
5
.github/FUNDING.yml
vendored
普通文件
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: joplin
|
||||
github: laurent22
|
||||
custom: https://joplinapp.org/donate/
|
||||
9
.github/ISSUE_TEMPLATE.md
vendored
普通文件
9
.github/ISSUE_TEMPLATE.md
vendored
普通文件
@@ -0,0 +1,9 @@
|
||||
👉 Please follow one of these issue templates:
|
||||
- https://github.com/laurent22/joplin/issues/new/choose
|
||||
|
||||
⚠️
|
||||
The GitHub issue tracker is for **bugs** and **security issues** ONLY. For feature requests and support, please use the forum:
|
||||
https://discourse.joplinapp.org/
|
||||
⚠️
|
||||
|
||||
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.
|
||||
62
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
普通文件
62
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
普通文件
@@ -0,0 +1,62 @@
|
||||
name: Bug Report
|
||||
description: Report a reproducible bug or regression in Joplin.
|
||||
labels: ['bug']
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: "Operating system"
|
||||
multiple: false
|
||||
options:
|
||||
- "Windows"
|
||||
- "macOS"
|
||||
- "Linux"
|
||||
- "Android"
|
||||
- "iOS"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: "Joplin version"
|
||||
placeholder: "For example 1.0.5"
|
||||
description: Please note that we will close the issue if the exact version is not provided, as we cannot help without this.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: desktop-about-content
|
||||
attributes:
|
||||
label: "Desktop version info"
|
||||
description: "If this issue is about the **desktop app**, please open the \"About\" dialog under the \"Help\" or \"Joplin\" menu and copy its content here."
|
||||
placeholder: "Joplin 2.13.5 (dev, darwin)\n\nClient ID: ..."
|
||||
|
||||
- type: textarea
|
||||
id: current
|
||||
attributes:
|
||||
label: Current behaviour
|
||||
description: What did Joplin do? Include screenshots and video recordings for UI problems if needed. If you are reporting a clipper bug, please include an example URL that shows the issue.
|
||||
placeholder: |
|
||||
1. This
|
||||
2. Then that
|
||||
3. Then this
|
||||
4. Etc.
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: What did you expect Joplin to do?
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: "If relevant, please provide a log file as described here: https://joplinapp.org/help/apps/debugging"
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
普通文件
8
.github/ISSUE_TEMPLATE/config.yml
vendored
普通文件
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Feature Requests
|
||||
url: https://discourse.joplinapp.org/c/features/
|
||||
about: Discuss ideas for new features or changes
|
||||
- name: Support
|
||||
url: https://discourse.joplinapp.org/c/support/
|
||||
about: Please ask for help here
|
||||
47
.github/PULL_REQUEST_TEMPLATE
vendored
普通文件
47
.github/PULL_REQUEST_TEMPLATE
vendored
普通文件
@@ -0,0 +1,47 @@
|
||||
<!--
|
||||
|
||||
Before contributing, please read the contribution guidelines: https://github.com/laurent22/joplin/blob/dev/readme/dev/index.md
|
||||
|
||||
If this is a Google Summer of Code pull request, please read the [GSoC pull request guidelines](https://github.com/joplin/gsoc/blob/master/pull_request_guidelines.md).
|
||||
|
||||
---
|
||||
|
||||
**Pull request title**: Please prefix the title with the area you are targeting, then add the issue you are addressing.
|
||||
|
||||
The format is:
|
||||
|
||||
<Prefix>: <Fixes|Resolves> #<issue>: <description>
|
||||
|
||||
Use "Resolves #123" for new features or improvements, and "Fixes #123" for bug fixes.
|
||||
|
||||
Examples of good titles:
|
||||
|
||||
- Desktop: Resolves #123: Added new setting to change font
|
||||
- Mobile, Desktop: Fixes #456: Fixed config screen error
|
||||
- Mobile, Desktop, Cli: Resolves #777: Improved note search performance
|
||||
|
||||
Valid prefixes:
|
||||
|
||||
- `Desktop` — the Windows/macOS/Linux app (Electron app)
|
||||
- `Mobile` — the mobile app (both Android and iOS)
|
||||
- `Android` — only the Android app
|
||||
- `iOS` — only the iOS app
|
||||
- `Web` — only the web app
|
||||
- `Windows` — Windows-specific changes
|
||||
- `Linux` — Linux-specific changes
|
||||
- `macOS` — macOS-specific changes
|
||||
- `Cli` — the command line app
|
||||
- `Server` — the Joplin Server
|
||||
- `Clipper` — the web clipper browser extension
|
||||
- `Transcribe` — the Transcribe server
|
||||
- `Plugins` — the plugin API or built-in plugins
|
||||
- `Api` — the data API
|
||||
- `Cloud` — Joplin Cloud
|
||||
- `Tools` — internal scripts and build tools
|
||||
- `CI` — continuous integration and GitHub workflows
|
||||
- `Doc` — documentation, README, website content
|
||||
- `Chore` — maintenance work that does not fit any of the above (dependency bumps, refactoring, cleanup)
|
||||
|
||||
If the change targets several areas, separate them with commas — for example "Mobile, Desktop, Cli: Resolves #777: Improved note search performance".
|
||||
|
||||
-->
|
||||
25
.github/lock.yml
vendored
普通文件
25
.github/lock.yml
vendored
普通文件
@@ -0,0 +1,25 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 7
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: false
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: false
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
# Repository to extend settings from
|
||||
# _extends: repo
|
||||
67
.github/scripts/build_windows.ps1
vendored
普通文件
67
.github/scripts/build_windows.ps1
vendored
普通文件
@@ -0,0 +1,67 @@
|
||||
# Force the postinstall build to run sequentially and retry `yarn install` a
|
||||
# few times. Parallel postinstall builds randomly crash on Windows with
|
||||
# STATUS_STACK_BUFFER_OVERRUN (0xC0000409), and node-pre-gyp prebuilt sqlite3
|
||||
# downloads from GitHub Releases occasionally fail. Setting the env vars here
|
||||
# (rather than only on the workflow step) ensures they reach every child
|
||||
# process spawned by `yarn install` -> postinstall -> gulp.
|
||||
function Install-WindowsDeps {
|
||||
$env:BUILD_SEQUENCIAL = '1'
|
||||
$env:IS_CONTINUOUS_INTEGRATION = '1'
|
||||
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') {
|
||||
# Deliberately not setting npm_config_build_from_source to force the
|
||||
# sqlite3 source build: sharp treats that variable as a boolean and
|
||||
# would then also build libvips from source, which fails. sqlite3
|
||||
# installs with --fallback-to-build, so it compiles by itself when the
|
||||
# win32-arm64 prebuilt is missing.
|
||||
|
||||
# Deliberately not installing the VSSetup PowerShell module here: with it
|
||||
# present, node-gyp probes Visual Studio via `Get-VSSetupInstance |
|
||||
# ConvertTo-Json`, whose output exceeds the default 1MB child process
|
||||
# buffer on the VS 2026 image. That fails with
|
||||
# ERR_CHILD_PROCESS_STDIO_MAXBUFFER and node-gyp then reports the
|
||||
# install as version "undefined". Without the module it falls through to
|
||||
# its own Find-VisualStudio.cs helper, which prints far less.
|
||||
}
|
||||
|
||||
$attempts = 3
|
||||
for ($i = 1; $i -le $attempts; $i++) {
|
||||
yarn install
|
||||
if ($LASTEXITCODE -eq 0) { return }
|
||||
if ($i -eq $attempts) { exit $LASTEXITCODE }
|
||||
Write-Host "yarn install failed (attempt $i/$attempts) - retrying..."
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
}
|
||||
|
||||
# Install dependencies and run `yarn dist` for the Windows desktop app.
|
||||
# Extra args are forwarded to `yarn dist` (e.g. --publish=never).
|
||||
#
|
||||
# Each runner builds only its own arch: cross compiling arm64 from x64 silently
|
||||
# ships x64 .node files, as @electron/rebuild doesn't resolve native modules
|
||||
# hoisted to the workspace root.
|
||||
# https://github.com/electron-userland/electron-builder/issues/10187
|
||||
function Build-WindowsApp {
|
||||
param([string[]]$DistArgs = @())
|
||||
|
||||
Install-WindowsDeps
|
||||
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') {
|
||||
# NSIS gets an `-arm64` suffix on its own, but the portable name is fixed
|
||||
# and the update metadata is `latest.yml` for every Windows arch. Both
|
||||
# would clobber the x64 runner's artifacts.
|
||||
# https://github.com/electron-userland/electron-builder/issues/6372
|
||||
$archArgs = @(
|
||||
'--arm64'
|
||||
'-c.portable.artifactName=${productName}Portable-arm64.${ext}'
|
||||
'-c.publish.provider=github'
|
||||
'-c.publish.channel=latest-win-arm64'
|
||||
)
|
||||
} else {
|
||||
$archArgs = @('--x64', '--ia32')
|
||||
}
|
||||
|
||||
cd packages/app-desktop
|
||||
yarn dist @archArgs @DistArgs
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
}
|
||||
163
.github/scripts/check_pr_title.js
vendored
普通文件
163
.github/scripts/check_pr_title.js
vendored
普通文件
@@ -0,0 +1,163 @@
|
||||
// Validates the PR title and acts on the result.
|
||||
//
|
||||
// - Renovate is filtered out at the workflow level (job `if:`).
|
||||
// - Translation-only PRs (every changed file is a .po) are skipped.
|
||||
// - Users in `softCheckUsers` get a relaxed check (issue number optional)
|
||||
// and only ever receive a comment, never a close.
|
||||
// - Everyone else must match the strict format. Invalid titles get a
|
||||
// comment and the PR is closed. We also apply a marker label so that
|
||||
// we can later tell our closures apart from any other closure.
|
||||
// - If the title becomes valid and the marker label is present, the PR
|
||||
// is reopened and the label is removed. Closures by humans (or by
|
||||
// another workflow) lack the label and are never overturned.
|
||||
//
|
||||
// Invoked from .github/workflows/check-pr-title.yml via actions/github-script.
|
||||
// Required inputs come from `env`: PR_AUTHOR, PR_NUMBER. The title is
|
||||
// fetched from the API rather than passed via env to avoid YAML expansion
|
||||
// silently stripping leading whitespace from `${{ ... }}`.
|
||||
|
||||
module.exports = async ({ github, context, core }) => {
|
||||
const softCheckUsers = ['laurent22', 'personalizedrefrigerator', 'mrjo118', 'tessus', 'CalebJohn', 'Rygaa'];
|
||||
const autoClosedLabel = 'auto-closed: invalid-title';
|
||||
|
||||
// Product-area prefixes are kept in sync with the list recognised by
|
||||
// packages/tools/git-changelog.ts so that valid titles are picked up by
|
||||
// the changelog generator. Non-product prefixes (Tools, Chore, CI, Doc,
|
||||
// Plugin Repo) are administrative categories for changes that do not
|
||||
// ship to users and so do not appear in the changelog.
|
||||
const prefix = '(Desktop|Mobile|Android|iOS|Web|Windows|Linux|macOS|Cli|Clipper|Server|Transcribe|Plugins|Api|Cloud|Tools|Chore|CI|Doc)';
|
||||
const prefixList = `${prefix}(,\\s*${prefix})*`;
|
||||
const strictRegex = new RegExp(`^${prefixList}: (Fixes|Resolves) #[0-9]+: .+`);
|
||||
const softRegex = new RegExp(`^${prefixList}: ((Fixes|Resolves) #[0-9]+: )?.+`);
|
||||
|
||||
const author = process.env.PR_AUTHOR;
|
||||
const prNumber = Number(process.env.PR_NUMBER);
|
||||
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
const title = pr.title;
|
||||
core.info(`Title (length=${title.length}): ${JSON.stringify(title)}`);
|
||||
|
||||
const isSoft = softCheckUsers.includes(author);
|
||||
|
||||
// listFiles returns up to 30 files per page; a pure translation PR is
|
||||
// small, so checking the first page is enough.
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
const isTranslationOnly = files.length > 0 && files.every(f => f.filename.endsWith('.po'));
|
||||
if (isTranslationOnly) {
|
||||
core.info('Translation-only PR — skipping title check.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Doc-only PRs do not require an issue number.
|
||||
const isDocOnly = /^Doc(,\s*Doc)*:/.test(title);
|
||||
const regex = isSoft || isDocOnly ? softRegex : strictRegex;
|
||||
if (regex.test(title)) {
|
||||
core.info('Title is valid.');
|
||||
|
||||
// If we previously closed this PR for an invalid title and the
|
||||
// title is now valid, reopen it. We only reopen if our marker
|
||||
// label is present, so closures by humans (or other workflows)
|
||||
// are never overturned. A maintainer can also remove the label
|
||||
// by hand to lock a PR closed regardless of future fixes.
|
||||
const wasAutoClosed = pr.state === 'closed' && pr.labels.some(l => l.name === autoClosedLabel);
|
||||
if (wasAutoClosed) {
|
||||
try {
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
state: 'open',
|
||||
});
|
||||
} catch (error) {
|
||||
// GitHub refuses to reopen a PR when another open PR
|
||||
// already exists from the same head→base branch pair.
|
||||
// In that case the contributor has already opened a
|
||||
// replacement, so leave this PR closed.
|
||||
if (error.status === 422) {
|
||||
core.info('Cannot reopen — another PR is already open from the same branch.');
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: autoClosedLabel,
|
||||
});
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: `@${author} thanks for fixing the title — this PR has been reopened.`,
|
||||
});
|
||||
core.info('PR reopened after title was fixed.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Hidden marker so we can detect a previous nag and avoid reposting on
|
||||
// every `synchronize` event (push to the PR branch).
|
||||
const commentMarker = '<!-- joplin-bot:invalid-pr-title -->';
|
||||
|
||||
const helpMessage = [
|
||||
commentMarker,
|
||||
`@${author} the pull request title does not match the required format.`,
|
||||
'',
|
||||
'Please prefix the title with the area you are targeting, then add the issue you are addressing. If the change targets several areas, separate them with commas. For example:',
|
||||
'',
|
||||
'- `Desktop: Resolves #123: Added new setting to change font`',
|
||||
'- `Mobile, Desktop: Fixes #456: Fixed config screen error`',
|
||||
'- `Mobile, Desktop, Cli: Resolves #777: Improved note search performance`',
|
||||
'',
|
||||
'See the [pull request template](https://github.com/laurent22/joplin/blob/dev/.github/PULL_REQUEST_TEMPLATE) for the list of valid prefixes and the full specification.',
|
||||
'',
|
||||
isSoft
|
||||
? '_This PR has been left open — please update the title when you have a moment._'
|
||||
: '_This PR has been closed automatically. Once you update the title to match the format above, the PR will be reopened automatically._',
|
||||
].join('\n');
|
||||
|
||||
const existingComments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
});
|
||||
const alreadyCommented = existingComments.some(c => c.body && c.body.includes(commentMarker));
|
||||
|
||||
if (!alreadyCommented) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: helpMessage,
|
||||
});
|
||||
} else {
|
||||
core.info('Invalid-title comment already posted — not commenting again.');
|
||||
}
|
||||
|
||||
if (!isSoft) {
|
||||
// Label first so the marker is set before the close event lands.
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: [autoClosedLabel],
|
||||
});
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
state: 'closed',
|
||||
});
|
||||
core.setFailed('Pull request title does not match the required format.');
|
||||
}
|
||||
};
|
||||
43
.github/scripts/publish_docker_manifest.sh
vendored
普通文件
43
.github/scripts/publish_docker_manifest.sh
vendored
普通文件
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION=$(echo "$GIT_TAG_NAME" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
||||
|
||||
echo "GIT_TAG_NAME=$GIT_TAG_NAME"
|
||||
echo "VERSION=$VERSION"
|
||||
echo "SERVER_TAG_PREFIX=$SERVER_TAG_PREFIX"
|
||||
echo "SERVER_REPOSITORY=$SERVER_REPOSITORY"
|
||||
|
||||
# Check if it's a server release, otherwise exit
|
||||
if [[ $GIT_TAG_NAME != $SERVER_TAG_PREFIX-* ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker manifest inspect $SERVER_REPOSITORY:arm64-$VERSION > /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Image $SERVER_REPOSITORY:arm64-$VERSION does not exist on the remote registry."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker manifest inspect $SERVER_REPOSITORY:amd64-$VERSION > /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Image $SERVER_REPOSITORY:amd64-$VERSION does not exist on the remote registry."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
docker manifest create $SERVER_REPOSITORY:$VERSION \
|
||||
$SERVER_REPOSITORY:arm64-$VERSION \
|
||||
$SERVER_REPOSITORY:amd64-$VERSION
|
||||
|
||||
docker manifest annotate $SERVER_REPOSITORY:$VERSION $SERVER_REPOSITORY:arm64-$VERSION --arch arm64
|
||||
docker manifest annotate $SERVER_REPOSITORY:$VERSION $SERVER_REPOSITORY:amd64-$VERSION --arch amd64
|
||||
|
||||
docker manifest push $SERVER_REPOSITORY:$VERSION
|
||||
|
||||
docker manifest create $SERVER_REPOSITORY:latest \
|
||||
$SERVER_REPOSITORY:arm64-$VERSION \
|
||||
$SERVER_REPOSITORY:amd64-$VERSION
|
||||
|
||||
docker manifest annotate $SERVER_REPOSITORY:latest $SERVER_REPOSITORY:arm64-$VERSION --arch arm64
|
||||
docker manifest annotate $SERVER_REPOSITORY:latest $SERVER_REPOSITORY:amd64-$VERSION --arch amd64
|
||||
|
||||
docker manifest push $SERVER_REPOSITORY:latest
|
||||
73
.github/scripts/repeat_changed_tests.sh
vendored
可执行文件
73
.github/scripts/repeat_changed_tests.sh
vendored
可执行文件
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
# Run the test files changed by a pull request several times, to catch flaky
|
||||
# tests before they are merged.
|
||||
#
|
||||
# Usage: repeat_changed_tests.sh <base_ref> [repeat_count]
|
||||
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
ROOT_DIR="$SCRIPT_DIR/../.."
|
||||
|
||||
BASE_REF="${1:-}"
|
||||
REPEAT_COUNT="${2:-5}"
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
# The CI checkout is shallow, so the base branch is not present locally. Always
|
||||
# fetch it rather than using a local branch of the same name, which may be
|
||||
# outdated.
|
||||
if git fetch --no-tags --depth=1 origin "$BASE_REF" 2> /dev/null; then
|
||||
BASE_REF=FETCH_HEAD
|
||||
elif ! git rev-parse --verify --quiet "$BASE_REF" > /dev/null; then
|
||||
echo "Could not resolve base ref \"$BASE_REF\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# `integration-tests` holds the Playwright specs (see app-desktop/playwright.config.ts),
|
||||
# which Jest cannot run. Other `.spec.*` files are Jest tests, so they are kept.
|
||||
testFiles=$(git diff --name-only --diff-filter=d "$BASE_REF" HEAD | grep -E '^packages/[^/]+/.*\.(test|spec)\.(ts|tsx|js|jsx)$' | grep -v -E '^packages/[^/]+/integration-tests/' || true)
|
||||
|
||||
if [ -z "$testFiles" ]; then
|
||||
echo "No test files were changed - skipping flaky test detection"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Changed test files:"
|
||||
echo "$testFiles" | sed 's/^/ /'
|
||||
|
||||
# Each package has its own Jest config and must be run from its own directory.
|
||||
packages=$(echo "$testFiles" | sed -E 's|^packages/([^/]+)/.*|\1|' | sort -u)
|
||||
|
||||
for package in $packages; do
|
||||
packageDir="$ROOT_DIR/packages/$package"
|
||||
|
||||
# Some packages have test files but no test runner configured.
|
||||
if ! node -e "const s = require('$packageDir/package.json').scripts; process.exit(s && s['test-ci'] ? 0 : 1)" 2> /dev/null; then
|
||||
echo "Package $package has no test-ci script - skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
# See packages/app-mobile/tools/runTestsConditionally.js
|
||||
if [ "$package" == "app-mobile" ] && [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo "Skipping app-mobile tests on macOS"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Pass the file names without their extension: some packages run the
|
||||
# TypeScript sources while others run the compiled files, and Jest matches
|
||||
# these patterns against the full path either way.
|
||||
packageTestNames=$(echo "$testFiles" | grep -E "^packages/$package/" | xargs -n 1 basename | sed -E 's#\.[a-z]+$##')
|
||||
|
||||
cd "$packageDir"
|
||||
for i in $(seq 1 "$REPEAT_COUNT"); do
|
||||
echo "Running $package tests - attempt $i/$REPEAT_COUNT..."
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if ! yarn jest $packageTestNames --forceExit; then
|
||||
echo "Tests failed on attempt $i/$REPEAT_COUNT in $package - they are flaky if earlier attempts passed"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
cd "$ROOT_DIR"
|
||||
done
|
||||
37
.github/scripts/retry.sh
vendored
可执行文件
37
.github/scripts/retry.sh
vendored
可执行文件
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
# Retry a command up to a few times. Used to absorb random CI flakes (network
|
||||
# failures, slow mirrors, etc).
|
||||
#
|
||||
# Usage: retry [attempts] [sleep_seconds] -- command [args...]
|
||||
# Both attempts and sleep_seconds are optional and default to 3 and 10.
|
||||
retry() {
|
||||
local attempts=3
|
||||
local sleep_seconds=10
|
||||
|
||||
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
attempts="$1"
|
||||
shift
|
||||
fi
|
||||
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
sleep_seconds="$1"
|
||||
shift
|
||||
fi
|
||||
if [ "$1" = '--' ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
local i=1
|
||||
while true; do
|
||||
"$@"
|
||||
local result=$?
|
||||
if [ $result -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ $i -ge $attempts ]; then
|
||||
return $result
|
||||
fi
|
||||
echo "\`$*\` failed (attempt $i/$attempts) - retrying in ${sleep_seconds}s..."
|
||||
i=$((i + 1))
|
||||
sleep "$sleep_seconds"
|
||||
done
|
||||
}
|
||||
383
.github/scripts/run_ci.sh
vendored
可执行文件
383
.github/scripts/run_ci.sh
vendored
可执行文件
@@ -0,0 +1,383 @@
|
||||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# Setup environment variables
|
||||
# =============================================================================
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
ROOT_DIR="$SCRIPT_DIR/../.."
|
||||
|
||||
TRANSCRIBE_TAG_PREFIX=transcribe
|
||||
TRANSCRIBE_REPOSITORY=joplin/transcribe
|
||||
|
||||
IS_PULL_REQUEST=0
|
||||
IS_DESKTOP_RELEASE=0
|
||||
IS_SERVER_RELEASE=0
|
||||
IS_TRANSCRIBE_RELEASE=0
|
||||
IS_LINUX=0
|
||||
IS_MACOS=0
|
||||
|
||||
# If pull requests are coming from a branch of the main repository,
|
||||
# IS_PULL_REQUEST will be zero.
|
||||
if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then
|
||||
IS_PULL_REQUEST=1
|
||||
fi
|
||||
|
||||
if [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
|
||||
IS_SERVER_RELEASE=1
|
||||
fi
|
||||
|
||||
if [[ $GIT_TAG_NAME = $TRANSCRIBE_TAG_PREFIX-* ]]; then
|
||||
IS_TRANSCRIBE_RELEASE=1
|
||||
fi
|
||||
|
||||
if [[ $GIT_TAG_NAME = v* ]]; then
|
||||
IS_DESKTOP_RELEASE=1
|
||||
fi
|
||||
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
IS_LINUX=1
|
||||
IS_MACOS=0
|
||||
else
|
||||
IS_LINUX=0
|
||||
IS_MACOS=1
|
||||
fi
|
||||
|
||||
DOCKER_IMAGE_PLATFORM="linux/amd64"
|
||||
|
||||
# Tests can randomly fail in some cases, so only run them when not publishing
|
||||
# a release
|
||||
RUN_TESTS=0
|
||||
|
||||
if [ "$IS_SERVER_RELEASE" = 0 ] && [ "$IS_DESKTOP_RELEASE" = 0 ] && [ "$IS_TRANSCRIBE_RELEASE" = 0 ]; then
|
||||
RUN_TESTS=1
|
||||
fi
|
||||
|
||||
if [ "$RUNNER_ARCH" == "ARM64" ]; then
|
||||
if [ "$IS_SERVER_RELEASE" == "0" ] && [ "$IS_TRANSCRIBE_RELEASE" == "0" ]; then
|
||||
# We exit now because nothing works properly with the ARM64 architecture.
|
||||
# We only proceed if building the server image.
|
||||
echo "Running on ARM64 and not trying to build server image - early exit"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$RUNNER_ARCH" == "ARM64" ]; then
|
||||
# Canvas is only needed for tests and it doesn't build in ARM64 so remove it
|
||||
RUN_TESTS=0
|
||||
cd "$ROOT_DIR/packages/lib"
|
||||
yarn remove canvas
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
DOCKER_IMAGE_PLATFORM="linux/arm64"
|
||||
|
||||
# Delete certain directories because `yarn install` will fail on ARM64.
|
||||
rm -rf app-desktop
|
||||
rm -rf app-mobile
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Print environment
|
||||
# =============================================================================
|
||||
|
||||
echo "RUNNER_OS=$RUNNER_OS"
|
||||
echo "RUNNER_ARCH=$RUNNER_ARCH"
|
||||
echo "GITHUB_WORKFLOW=$GITHUB_WORKFLOW"
|
||||
echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME"
|
||||
echo "GITHUB_REF=$GITHUB_REF"
|
||||
echo "RUNNER_OS=$RUNNER_OS"
|
||||
echo "GIT_TAG_NAME=$GIT_TAG_NAME"
|
||||
echo "BUILD_SEQUENCIAL=$BUILD_SEQUENCIAL"
|
||||
echo "SERVER_REPOSITORY=$SERVER_REPOSITORY"
|
||||
echo "SERVER_TAG_PREFIX=$SERVER_TAG_PREFIX"
|
||||
echo "TRANSCRIBE_TAG_PREFIX=$TRANSCRIBE_TAG_PREFIX"
|
||||
echo "DOCKER_IMAGE_PLATFORM=$DOCKER_IMAGE_PLATFORM"
|
||||
|
||||
echo "IS_CONTINUOUS_INTEGRATION=$IS_CONTINUOUS_INTEGRATION"
|
||||
echo "IS_PULL_REQUEST=$IS_PULL_REQUEST"
|
||||
echo "IS_DESKTOP_RELEASE=$IS_DESKTOP_RELEASE"
|
||||
echo "IS_SERVER_RELEASE=$IS_SERVER_RELEASE"
|
||||
echo "IS_TRANSCRIBE_RELEASE=$IS_TRANSCRIBE_RELEASE"
|
||||
echo "RUN_TESTS=$RUN_TESTS"
|
||||
echo "IS_LINUX=$IS_LINUX"
|
||||
echo "IS_MACOS=$IS_MACOS"
|
||||
|
||||
echo "Node $( node -v )"
|
||||
echo "Npm $( npm -v )"
|
||||
echo "Yarn $( yarn -v )"
|
||||
echo "Rust $( rustc --version )"
|
||||
|
||||
# =============================================================================
|
||||
# Install packages
|
||||
# =============================================================================
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
source "$(dirname "$0")/retry.sh"
|
||||
|
||||
retry yarn install
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
echo "Yarn installation failed. Search for 'exit code 1' in the log for more information."
|
||||
exit $testResult
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Run test units
|
||||
# =============================================================================
|
||||
|
||||
if [ "$RUN_TESTS" == "1" ]; then
|
||||
echo "Step: Running tests..."
|
||||
|
||||
# On Linux, we run the Joplin Server tests using PostgreSQL
|
||||
if [ "$IS_LINUX" == "1" ]; then
|
||||
echo "Running Joplin Server tests using PostgreSQL..."
|
||||
# Docker Hub auth/pulls occasionally time out on CI, so retry a few
|
||||
# times before giving up.
|
||||
cmdResult=1
|
||||
for attempt in 1 2 3; do
|
||||
sudo docker compose --parallel 1 --file docker-compose.db-dev.yml up -d
|
||||
cmdResult=$?
|
||||
if [ $cmdResult -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
echo "docker compose up failed (attempt $attempt). Retrying in 15s..."
|
||||
sudo docker compose --file docker-compose.db-dev.yml down --remove-orphans || true
|
||||
sleep 15
|
||||
done
|
||||
if [ $cmdResult -ne 0 ]; then
|
||||
exit $cmdResult
|
||||
fi
|
||||
export JOPLIN_TESTS_SERVER_DB=pg
|
||||
else
|
||||
echo "Running Joplin Server tests using SQLite..."
|
||||
fi
|
||||
|
||||
# Need this because we're getting this error:
|
||||
#
|
||||
# @joplin/lib: FATAL ERROR: Ineffective mark-compacts near heap limit
|
||||
# Allocation failed - JavaScript heap out of memory
|
||||
#
|
||||
# https://stackoverflow.com/questions/38558989
|
||||
export NODE_OPTIONS="--max-old-space-size=32768"
|
||||
yarn test-ci
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
|
||||
# Run the test files changed by the pull request several times, to catch
|
||||
# flaky tests before they are merged.
|
||||
if [ "$IS_PULL_REQUEST" == "1" ]; then
|
||||
echo "Step: Checking changed tests for flakiness..."
|
||||
|
||||
"$SCRIPT_DIR/repeat_changed_tests.sh" "$GITHUB_BASE_REF"
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Run linter for pull requests only. We also don't want this to make the desktop
|
||||
# release randomly fail.
|
||||
# =============================================================================
|
||||
|
||||
if [ "$RUN_TESTS" == "1" ]; then
|
||||
echo "Step: Running linter..."
|
||||
|
||||
yarn linter-ci ./
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
|
||||
yarn packageJsonLint
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Validate translations - this is needed as some users manually edit .po files
|
||||
# (and often make mistakes) instead of using a proper tool like poedit. Doing it
|
||||
# for Linux only is sufficient.
|
||||
# =============================================================================
|
||||
|
||||
if [ "$IS_LINUX" == "1" ]; then
|
||||
echo "Step: Validating translations..."
|
||||
|
||||
node packages/tools/validate-translation.js
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Check that we didn't lose any string due to gettext not being able to parse
|
||||
# newly modified or added scripts. This is convenient to quickly view on GitHub
|
||||
# what commit may have broken translation building.
|
||||
# =============================================================================
|
||||
|
||||
if [ "$RUN_TESTS" == "1" ]; then
|
||||
if [ "$IS_LINUX" == "1" ]; then
|
||||
echo "Step: Checking for lost translation strings..."
|
||||
|
||||
xgettext --version
|
||||
|
||||
node packages/tools/build-translation.js --missing-strings-check-only
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Check .gitignore and .ignore.eslint files - they should be updated when
|
||||
# new TypeScript files are added by running `yarn updateIgnored`.
|
||||
# See coding_style.md
|
||||
# =============================================================================
|
||||
|
||||
if [ "$IS_LINUX" == "1" ]; then
|
||||
echo "Step: Checking for files that should have been ignored..."
|
||||
|
||||
# .gitignore and .ignore.eslint can be modified during yarn install. Reset them
|
||||
# so that checkIgnoredFiles works.
|
||||
git restore .gitignore .ignore.eslint
|
||||
|
||||
node packages/tools/checkIgnoredFiles.js
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Check that the website still builds
|
||||
# =============================================================================
|
||||
|
||||
if [ "$RUN_TESTS" == "1" ]; then
|
||||
if [ "$IS_LINUX" == "1" ]; then
|
||||
echo "Step: Check that the website still builds..."
|
||||
|
||||
mkdir -p ../joplin-website/docs
|
||||
CROWDIN_PERSONAL_TOKEN="$CROWDIN_PERSONAL_TOKEN" yarn crowdinDownload
|
||||
SKIP_SPONSOR_PROCESSING=1 yarn buildWebsite
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Spellchecking
|
||||
# =============================================================================
|
||||
|
||||
if [ "$IS_LINUX" == "1" ]; then
|
||||
echo "Step: Spellchecking..."
|
||||
|
||||
yarn spellcheck --all
|
||||
testResult=$?
|
||||
if [ $testResult -ne 0 ]; then
|
||||
exit $testResult
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Find out if we should run the build or not. Electron-builder gets stuck when
|
||||
# building PRs so we disable it in this case. The Linux build should provide
|
||||
# enough info if the app builds or not.
|
||||
# https://github.com/electron-userland/electron-builder/issues/4263
|
||||
# =============================================================================
|
||||
|
||||
if [ "$IS_PULL_REQUEST" == "1" ]; then
|
||||
if [ "$IS_MACOS" == "1" ]; then
|
||||
echo "Step: Not building Electron app"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Build the Electron app or Docker image depending on the current tag.
|
||||
#
|
||||
# If the current tag is a desktop release tag (starts with "v", such as
|
||||
# "v1.4.7"), we build and publish to GitHub. Otherwise we only build but don't
|
||||
# publish to GitHub. It helps finding out any issue in pull requests and dev
|
||||
# branch.
|
||||
# =============================================================================
|
||||
|
||||
cd "$ROOT_DIR/packages/app-desktop"
|
||||
|
||||
# On macOS, `yarn dist` can fail randomly with `hdiutil detach ... Resource
|
||||
# busy` when packaging the DMG, because another process (Spotlight mds,
|
||||
# diskimages-helper, etc.) still holds the mounted image. Retry the whole
|
||||
# build a couple of times to absorb the flake.
|
||||
run_yarn_dist() {
|
||||
local attempts=3
|
||||
local i=1
|
||||
while true; do
|
||||
USE_HARD_LINKS=false yarn dist "$@"
|
||||
local result=$?
|
||||
if [ $result -eq 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$IS_MACOS" != "1" ] || [ $i -ge $attempts ]; then
|
||||
return $result
|
||||
fi
|
||||
echo "yarn dist failed (attempt $i/$attempts) - retrying..."
|
||||
i=$((i + 1))
|
||||
sleep 10
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$IS_DESKTOP_RELEASE" == "1" ]; then
|
||||
echo "Step: Building and publishing desktop application..."
|
||||
# cd "$ROOT_DIR/packages/tools"
|
||||
# node bundleDefaultPlugins.js
|
||||
cd "$ROOT_DIR/packages/app-desktop"
|
||||
|
||||
if [ "$IS_MACOS" == "1" ]; then
|
||||
# This is to fix this error:
|
||||
#
|
||||
# Exit code: ENOENT. spawn /usr/bin/python ENOENT
|
||||
#
|
||||
# Ref: https://github.com/electron-userland/electron-builder/issues/6767#issuecomment-1096589528
|
||||
#
|
||||
# It can be removed once we upgrade to electron-builder@23, however we
|
||||
# cannot currently do this due to this error:
|
||||
# https://github.com/laurent22/joplin/issues/8149
|
||||
#
|
||||
# electron-builder@24, however, still expects the python binary to be named
|
||||
# "python" and seems to no longer respect the PYTHON_PATH environment variable.
|
||||
# We work around this by aliasing python.
|
||||
alias python=$(which python3)
|
||||
fi
|
||||
run_yarn_dist
|
||||
elif [[ $IS_LINUX = 1 ]] && [ "$IS_SERVER_RELEASE" == "1" ]; then
|
||||
echo "Step: Building Joplin Server Docker Image..."
|
||||
cd "$ROOT_DIR"
|
||||
yarn buildServerDocker --docker-file Dockerfile.server --platform $DOCKER_IMAGE_PLATFORM --tag-name $GIT_TAG_NAME --push-images --repository $SERVER_REPOSITORY
|
||||
elif [[ $IS_LINUX = 1 ]] && [ "$IS_TRANSCRIBE_RELEASE" == "1" ]; then
|
||||
echo "Step: Building Joplin Transcribe Docker Image..."
|
||||
cd "$ROOT_DIR"
|
||||
yarn buildServerDocker --docker-file Dockerfile.transcribe --platform $DOCKER_IMAGE_PLATFORM --tag-name $GIT_TAG_NAME --push-images --repository $TRANSCRIBE_REPOSITORY
|
||||
else
|
||||
echo "Step: Building but *not* publishing desktop application..."
|
||||
|
||||
if [ "$IS_MACOS" == "1" ]; then
|
||||
# See above why we need to specify Python
|
||||
alias python=$(which python3)
|
||||
|
||||
# We also want to disable signing the app in this case, because
|
||||
# it randomly fails and we don't even need it
|
||||
# https://www.electron.build/code-signing#how-to-disable-code-signing-during-the-build-process-on-macos
|
||||
export CSC_IDENTITY_AUTO_DISCOVERY=false
|
||||
npm pkg set 'build.mac.identity'=null --json
|
||||
fi
|
||||
run_yarn_dist --publish=never
|
||||
fi
|
||||
25
.github/workflows/automerge.yml
vendored
普通文件
25
.github/workflows/automerge.yml
vendored
普通文件
@@ -0,0 +1,25 @@
|
||||
name: automerge
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/10 * * * *'
|
||||
jobs:
|
||||
|
||||
# This job will make the action fail if any of the checks hasn't passed
|
||||
# https://github.com/marketplace/actions/allcheckspassed
|
||||
# allchecks:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: wechuli/allcheckspassed@v1
|
||||
|
||||
automerge:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- id: automerge
|
||||
name: automerge
|
||||
uses: "pascalgn/automerge-action@v0.16.4"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
MERGE_METHOD: "squash"
|
||||
LOG: "DEBUG"
|
||||
84
.github/workflows/build-android.yml
vendored
普通文件
84
.github/workflows/build-android.yml
vendored
普通文件
@@ -0,0 +1,84 @@
|
||||
# The goal of this action is to compile the Android debug build. That should
|
||||
# tell us automatically if something got broken when a dependency was changed.
|
||||
|
||||
name: react-native-android-build-apk
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
AssembleRelease:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y libsecret-1-dev
|
||||
|
||||
# Free disk space before anything else so the Gradle/NDK transforms
|
||||
# (which can write 20GB+ during react-native-quick-crypto's OpenSSL
|
||||
# build) don't exhaust the runner's ~14GB free space. Keep the Android
|
||||
# SDK and Java toolchains; remove the large preinstalled packages this
|
||||
# workflow doesn't use.
|
||||
- name: Free disk space
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /usr/share/swift || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
sudo rm -rf /opt/hostedtoolcache/CodeQL || true
|
||||
sudo rm -rf /usr/local/share/boost || true
|
||||
sudo rm -rf /usr/local/share/powershell || true
|
||||
docker system prune -af || true
|
||||
df -h /
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '20'
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'yarn'
|
||||
|
||||
- uses: ./.github/workflows/shared/install-yarn
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
source "${GITHUB_WORKSPACE}/.github/scripts/retry.sh"
|
||||
retry yarn install
|
||||
env:
|
||||
SKIP_ONENOTE_CONVERTER_BUILD: 1
|
||||
|
||||
- name: Assemble Android Release
|
||||
run: |
|
||||
cd packages/app-mobile/android
|
||||
sed -i -- 's/signingConfig signingConfigs.release/signingConfig signingConfigs.debug/' app/build.gradle
|
||||
./gradlew assembleRelease
|
||||
|
||||
- name: Verify alignment
|
||||
run: |
|
||||
cd packages/app-mobile/android/app
|
||||
APK_FILE="./build/outputs/apk/release/app-release.apk"
|
||||
if test ! -f "$APK_FILE" ; then
|
||||
echo "APK file not found."
|
||||
exit 1
|
||||
else
|
||||
echo "APK file found at: $APK_FILE"
|
||||
fi
|
||||
|
||||
BUILD_TOOLS_PATH="$ANDROID_HOME/build-tools/"
|
||||
if test ! -d "$BUILD_TOOLS_PATH" ; then
|
||||
echo "Build tools not found at $BUILD_TOOLS_PATH ($ANDROID_HOME, $BUILD_TOOLS_VERSION)"
|
||||
exit 1
|
||||
fi
|
||||
# The build-tools/ directory contains different subdirectories
|
||||
# for each build tools version. As a result, there may be multiple
|
||||
# zipalign tools. Select the most recent (biggest two-digit version number):
|
||||
ZIPALIGN_PATH="$(find $BUILD_TOOLS_PATH -name "zipalign" -print | sort | tail -n1)"
|
||||
if test ! -x "$ZIPALIGN_PATH" ; then
|
||||
echo "zipalign not found (searching in $BUILD_TOOLS_PATH, candidate: $ZIPALIGN_PATH)"
|
||||
exit 1
|
||||
fi
|
||||
"$ZIPALIGN_PATH" -c -P 16 -v 4 "$APK_FILE"
|
||||
94
.github/workflows/build-macos-m1.yml
vendored
普通文件
94
.github/workflows/build-macos-m1.yml
vendored
普通文件
@@ -0,0 +1,94 @@
|
||||
name: Build macOS M1
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Main:
|
||||
# We always process desktop release tags, because they also publish the release
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: olegtarasov/get-tag@v2.1.4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'yarn'
|
||||
|
||||
- uses: ./.github/workflows/shared/install-yarn
|
||||
|
||||
- name: Install macOs dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
# Required for building the canvas package
|
||||
brew install pango
|
||||
|
||||
# See github-action-main.yml for explanation
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Set Publish Flag
|
||||
run: |
|
||||
if [[ $GIT_TAG_NAME = v* ]]; then
|
||||
echo "PUBLISH_ENABLED=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "PUBLISH_ENABLED=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build macOS M1 app
|
||||
env:
|
||||
APPLE_ASC_PROVIDER: ${{ secrets.APPLE_ASC_PROVIDER }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
PUBLISH_ENABLED: ${{ env.PUBLISH_ENABLED }}
|
||||
run: |
|
||||
export npm_config_arch=arm64
|
||||
export npm_config_target_arch=arm64
|
||||
source "${GITHUB_WORKSPACE}/.github/scripts/retry.sh"
|
||||
retry yarn install
|
||||
cd packages/app-desktop
|
||||
npm pkg set 'build.mac.artifactName'='${productName}-${version}-${arch}.${ext}'
|
||||
|
||||
npm pkg delete 'build.mac.target'
|
||||
npm pkg set 'build.mac.target[0].target'='dmg'
|
||||
npm pkg set 'build.mac.target[0].arch[0]'='arm64'
|
||||
npm pkg set 'build.mac.target[1].target'='zip'
|
||||
npm pkg set 'build.mac.target[1].arch[0]'='arm64'
|
||||
|
||||
# Only enable pkg build in the main repository CI. As of 01/15/2026, pkg
|
||||
# build fails when running on external pull requests.
|
||||
if [[ "$GITHUB_EVENT_NAME" != "pull_request" ]]; then
|
||||
npm pkg set 'build.mac.target[2].target'='pkg'
|
||||
npm pkg set 'build.mac.target[2].arch[0]'='arm64'
|
||||
fi
|
||||
|
||||
build_dist() {
|
||||
if [[ "$PUBLISH_ENABLED" == "true" ]]; then
|
||||
echo "Building and publishing desktop application..."
|
||||
PYTHON_PATH=$(which python) USE_HARD_LINKS=false yarn dist --mac --arm64
|
||||
|
||||
yarn modifyReleaseAssets --repo="$GH_REPO" --tag="$GIT_TAG_NAME" --token="$GITHUB_TOKEN"
|
||||
else
|
||||
echo "Building but *not* publishing desktop application..."
|
||||
|
||||
# We also want to disable signing the app in this case, because
|
||||
# it doesn't work and we don't need it.
|
||||
# https://www.electron.build/code-signing#how-to-disable-code-signing-during-the-build-process-on-macos
|
||||
|
||||
export CSC_IDENTITY_AUTO_DISCOVERY=false
|
||||
npm pkg set 'build.mac.identity'=null --json
|
||||
|
||||
PYTHON_PATH=$(which python) USE_HARD_LINKS=false yarn dist --mac --arm64 --publish=never
|
||||
fi
|
||||
}
|
||||
|
||||
build_dist || build_dist
|
||||
31
.github/workflows/check-pr-title.yml
vendored
普通文件
31
.github/workflows/check-pr-title.yml
vendored
普通文件
@@ -0,0 +1,31 @@
|
||||
name: Check pull request title
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
# Skip the check entirely for these automation accounts.
|
||||
if: github.event.pull_request.user.login != 'renovate[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
# Sparse checkout so we only pull the script, not the full repo.
|
||||
# `pull_request_target` checks out the base branch by default,
|
||||
# which is what we want — we never execute PR-supplied code.
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: .github/scripts
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Check title
|
||||
uses: actions/github-script@v9
|
||||
env:
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
with:
|
||||
script: |
|
||||
const check = require('./.github/scripts/check_pr_title.js');
|
||||
await check({ github, context, core });
|
||||
37
.github/workflows/cla.yml
vendored
普通文件
37
.github/workflows/cla.yml
vendored
普通文件
@@ -0,0 +1,37 @@
|
||||
name: "CLA Assistant"
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_target:
|
||||
types: [opened,closed,synchronize]
|
||||
|
||||
jobs:
|
||||
CLAAssistant:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "CLA Assistant"
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
||||
# Beta Release
|
||||
uses: contributor-assistant/github-action@v2.6.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# the below token should have repo scope and must be manually added by you in the repository's secret
|
||||
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
with:
|
||||
path-to-signatures: 'readme/cla/signatures.json'
|
||||
path-to-document: 'https://github.com/laurent22/joplin/blob/dev/readme/cla.md' # e.g. a CLA or a DCO document
|
||||
# branch should not be protected
|
||||
branch: 'cla_signatures'
|
||||
allowlist: joplinbot,renovate[bot]
|
||||
|
||||
# the followings are the optional inputs - If the optional inputs are not given, then default values will be taken
|
||||
#remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository)
|
||||
#remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository)
|
||||
#create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
|
||||
#signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
|
||||
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
|
||||
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
|
||||
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
|
||||
lock-pullrequest-aftermerge: false
|
||||
#use-dco-flag: true - If you are using DCO instead of CLA
|
||||
24
.github/workflows/close-stale-issues.yml
vendored
普通文件
24
.github/workflows/close-stale-issues.yml
vendored
普通文件
@@ -0,0 +1,24 @@
|
||||
name: 'Close stale issues'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 16 * * *'
|
||||
permissions:
|
||||
issues: write
|
||||
jobs:
|
||||
ProcessStaleIssues:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
# Use this to do a dry run from a pull request
|
||||
# debug-only: true
|
||||
stale-issue-message: "Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? If you require support or are requesting an enhancement or feature then please create a topic on the [Joplin forum](https://discourse.joplinapp.org/). This issue may be closed if no further activity occurs. You may comment on the issue and I will leave it open. Thank you for your contributions."
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
operations-per-run: 1000
|
||||
exempt-issue-labels: 'good first issue,upstream,backlog,high,medium,spec,cannot reproduce,enhancement'
|
||||
stale-issue-label: 'stale'
|
||||
close-issue-message: 'Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, feel free to create a new issue with up-to-date information.'
|
||||
# Don't process pull requests at all
|
||||
days-before-pr-stale: -1
|
||||
14
.github/workflows/comment-on-failure.yml
vendored
普通文件
14
.github/workflows/comment-on-failure.yml
vendored
普通文件
@@ -0,0 +1,14 @@
|
||||
name: comment-on-failure
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- Joplin Continuous Integration
|
||||
- react-native-android-build-apk
|
||||
- Build macOS M1
|
||||
types: [ completed ]
|
||||
|
||||
jobs:
|
||||
comment-failure:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: quipper/comment-failure-action@v0.1.1
|
||||
67
.github/workflows/delete-coderabbit-comments.yml
vendored
普通文件
67
.github/workflows/delete-coderabbit-comments.yml
vendored
普通文件
@@ -0,0 +1,67 @@
|
||||
name: Delete CodeRabbit PR Comments
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
delete-coderabbit-comments:
|
||||
if: >
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
(github.event.comment.user.login == 'coderabbitai' || github.event.comment.user.login == 'coderabbitai[bot]'))
|
||||
||
|
||||
(github.event_name == 'pull_request_review' &&
|
||||
(github.event.review.user.login == 'coderabbitai' || github.event.review.user.login == 'coderabbitai[bot]'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Fork PRs trigger `pull_request_review` without exposing repo secrets,
|
||||
# so the App token can't be minted. Skip the job cleanly in that case
|
||||
# instead of letting it fail loudly.
|
||||
- name: Check secrets availability
|
||||
id: check-secrets
|
||||
env:
|
||||
APP_ID: ${{ secrets.CODERABBIT_CLEANUP_APP_ID }}
|
||||
run: |
|
||||
if [ -z "$APP_ID" ]; then
|
||||
echo "App secrets not available (likely a fork PR event); skipping."
|
||||
echo "available=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "available=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Generate GitHub App token
|
||||
if: steps.check-secrets.outputs.available == 'true'
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v3
|
||||
with:
|
||||
app-id: ${{ secrets.CODERABBIT_CLEANUP_APP_ID }}
|
||||
private-key: ${{ secrets.CODERABBIT_CLEANUP_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Clean up CodeRabbit comments
|
||||
if: steps.check-secrets.outputs.available == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
run: |
|
||||
# Clear the body of all CodeRabbit reviews on this PR
|
||||
# (We can't delete reviews, but clearing the body removes the spam
|
||||
# while keeping inline code comments visible)
|
||||
REVIEW_IDS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/reviews" \
|
||||
--jq '[.[] | select(.user.login == "coderabbitai[bot]" and .body != "") | .id] | .[]')
|
||||
|
||||
for REVIEW_ID in $REVIEW_IDS; do
|
||||
gh api -X PUT "repos/${REPO}/pulls/${PR_NUMBER}/reviews/${REVIEW_ID}" \
|
||||
-f body="."
|
||||
done
|
||||
|
||||
# Delete all CodeRabbit issue comments on this PR
|
||||
COMMENT_URLS=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/comments?per_page=100" \
|
||||
--jq '[.[] | select(.user.login == "coderabbitai[bot]") | .url] | .[]')
|
||||
|
||||
for URL in $COMMENT_URLS; do
|
||||
gh api -X DELETE "$URL"
|
||||
done
|
||||
227
.github/workflows/github-actions-main.yml
vendored
普通文件
227
.github/workflows/github-actions-main.yml
vendored
普通文件
@@ -0,0 +1,227 @@
|
||||
name: Joplin Continuous Integration
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Main:
|
||||
# We always process server or desktop release tags, because they also publish the release
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
# Do not use unbuntu-latest because it causes `The operation was canceled` failures:
|
||||
# https://github.com/actions/runner-images/issues/6709
|
||||
#
|
||||
# windows-11-vs2026-arm is pinned because windows-11-arm migrates to
|
||||
# Visual Studio 2026 in late September 2026, and that job compiles
|
||||
# sqlite3 from source.
|
||||
# https://github.com/actions/runner-images/issues/14602
|
||||
os: [macos-15-intel, ubuntu-22.04, windows-2025, ubuntu-22.04-arm, windows-11-vs2026-arm]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup build environment
|
||||
uses: ./.github/workflows/shared/setup-build-environment
|
||||
|
||||
- name: Install Docker Engine
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get install -y apt-transport-https
|
||||
sudo apt-get install -y ca-certificates
|
||||
sudo apt-get install -y curl
|
||||
sudo apt-get install -y gnupg
|
||||
sudo apt-get install -y lsb-release
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
|
||||
|
||||
- name: Free disk space
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
docker system prune -af || true
|
||||
docker builder prune -af || true
|
||||
sudo rm -rf /var/lib/docker/tmp/* || true
|
||||
|
||||
# Login to Docker only if we're on a server release tag. If we run this on
|
||||
# a pull request it will fail because the PR doesn't have access to
|
||||
# secrets
|
||||
- uses: docker/login-action@v4
|
||||
if: runner.os == 'Linux' && (startsWith(github.ref, 'refs/tags/server-v') || startsWith(github.ref, 'refs/tags/transcribe-v'))
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# - name: Test Windows app signing
|
||||
# if: runner.os == 'Windows'
|
||||
# env:
|
||||
# GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
# IS_CONTINUOUS_INTEGRATION: 1
|
||||
# BUILD_SEQUENCIAL: 1
|
||||
# SSL_ESIGNER_USER_NAME: ${{ secrets.SSL_ESIGNER_USER_NAME }}
|
||||
# SSL_ESIGNER_USER_PASSWORD: ${{ secrets.SSL_ESIGNER_USER_PASSWORD }}
|
||||
# SSL_ESIGNER_CREDENTIAL_ID: ${{ secrets.SSL_ESIGNER_CREDENTIAL_ID }}
|
||||
# SSL_ESIGNER_USER_TOTP: ${{ secrets.SSL_ESIGNER_USER_TOTP }}
|
||||
# SIGN_APPLICATION: 1
|
||||
# # To ensure that the operations stop on failure, all commands
|
||||
# # should be on one line with "&&" in between.
|
||||
# run: |
|
||||
# yarn install && cd packages/app-desktop && yarn dist
|
||||
|
||||
- name: Run tests, build and publish Linux and macOS apps
|
||||
if: runner.os == 'Linux' || runner.os == 'macOs'
|
||||
env:
|
||||
APPLE_ASC_PROVIDER: ${{ secrets.APPLE_ASC_PROVIDER }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
SERVER_REPOSITORY: joplin/server
|
||||
SERVER_TAG_PREFIX: server
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
run: |
|
||||
"${GITHUB_WORKSPACE}/.github/scripts/run_ci.sh"
|
||||
|
||||
- name: Build and publish Windows app
|
||||
if: runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
SSL_ESIGNER_USER_NAME: ${{ secrets.SSL_ESIGNER_USER_NAME }}
|
||||
SSL_ESIGNER_USER_PASSWORD: ${{ secrets.SSL_ESIGNER_USER_PASSWORD }}
|
||||
SSL_ESIGNER_CREDENTIAL_ID: ${{ secrets.SSL_ESIGNER_CREDENTIAL_ID }}
|
||||
SSL_ESIGNER_USER_TOTP: ${{ secrets.SSL_ESIGNER_USER_TOTP }}
|
||||
SIGN_APPLICATION: 1
|
||||
# `yarn install` can fail randomly on CI when node-pre-gyp downloads
|
||||
# prebuilt sqlite3 binaries from GitHub Releases - retry a few times.
|
||||
run: |
|
||||
. "${env:GITHUB_WORKSPACE}/.github/scripts/build_windows.ps1"
|
||||
Build-WindowsApp
|
||||
|
||||
# Build and package the Windows app, without publishing it, just to
|
||||
# verify that the build process hasn't been broken.
|
||||
- name: Build Windows app (no publishing)
|
||||
if: runner.os == 'Windows' && !startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
IS_CONTINUOUS_INTEGRATION: 1
|
||||
BUILD_SEQUENCIAL: 1
|
||||
SERVER_REPOSITORY: joplin/server
|
||||
SERVER_TAG_PREFIX: server
|
||||
run: |
|
||||
. "${env:GITHUB_WORKSPACE}/.github/scripts/build_windows.ps1"
|
||||
Build-WindowsApp -DistArgs "--publish=never"
|
||||
|
||||
- name: Publish Docker manifest
|
||||
if: runner.os == 'Linux'
|
||||
env:
|
||||
SERVER_REPOSITORY: joplin/server
|
||||
SERVER_TAG_PREFIX: server
|
||||
run: |
|
||||
chmod 700 "${GITHUB_WORKSPACE}/.github/scripts/publish_docker_manifest.sh"
|
||||
"${GITHUB_WORKSPACE}/.github/scripts/publish_docker_manifest.sh"
|
||||
|
||||
ServerDockerImage:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
# Do not use unbuntu-latest because it causes `The operation was canceled` failures:
|
||||
# https://github.com/actions/runner-images/issues/6709
|
||||
os: [ubuntu-22.04, ubuntu-22.04-arm]
|
||||
steps:
|
||||
|
||||
- name: Install Docker Engine
|
||||
run: |
|
||||
sudo apt-get install -y apt-transport-https
|
||||
sudo apt-get install -y ca-certificates
|
||||
sudo apt-get install -y curl
|
||||
sudo apt-get install -y gnupg
|
||||
sudo apt-get install -y lsb-release
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
|
||||
- name: Free disk space
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo rm -rf /usr/local/lib/android || true
|
||||
sudo rm -rf /usr/share/dotnet || true
|
||||
sudo rm -rf /opt/ghc || true
|
||||
docker system prune -af || true
|
||||
docker builder prune -af || true
|
||||
sudo rm -rf /var/lib/docker/tmp/* || true
|
||||
|
||||
- uses: ./.github/workflows/shared/install-yarn
|
||||
|
||||
- name: Build Docker Image
|
||||
env:
|
||||
BUILD_SEQUENCIAL: 1
|
||||
run: |
|
||||
if [ "$RUNNER_ARCH" == "ARM64" ]; then
|
||||
DOCKER_IMAGE_PLATFORM="linux/arm64"
|
||||
fi
|
||||
|
||||
echo "RUNNER_OS=$RUNNER_OS"
|
||||
echo "RUNNER_ARCH=$RUNNER_ARCH"
|
||||
echo "DOCKER_IMAGE_PLATFORM=$DOCKER_IMAGE_PLATFORM"
|
||||
|
||||
source "${GITHUB_WORKSPACE}/.github/scripts/retry.sh"
|
||||
retry yarn install
|
||||
yarn buildServerDocker --docker-file Dockerfile.server --platform $DOCKER_IMAGE_PLATFORM --tag-name server-v0.0.0 --repository joplin/server
|
||||
|
||||
# Basic test to ensure that the created build is valid. It should exit with
|
||||
# code 0 if it works.
|
||||
docker run joplin/server:$(dpkg --print-architecture)-0.0.0 node dist/index.js migrate list
|
||||
|
||||
- name: Check HTTP request
|
||||
run: |
|
||||
# Need to pass environment variables:
|
||||
docker run --env MAX_TIME_DRIFT=0 --publish 22300:22300 joplin/server:$(dpkg --print-architecture)-0.0.0 node dist/index.js --env dev &
|
||||
|
||||
# Wait for server to start
|
||||
sleep 120
|
||||
|
||||
# Check if status code is correct
|
||||
# if the actual_status DOES NOT include the expected_status
|
||||
# it exits the process with code 1
|
||||
|
||||
expected_status="HTTP/1.1 200 OK"
|
||||
actual_status=$(curl -I -X GET http://localhost:22300/api/ping | head -n 1)
|
||||
if [[ ! "$actual_status" =~ "$expected_status" ]]; then
|
||||
echo 'Failed while checking the status code after request to /api/ping'
|
||||
echo 'expected: ' $expected_status
|
||||
echo 'actual: ' $actual_status
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Check if the body response is correct
|
||||
# if the actual_body is different of expected_body exit with code 1
|
||||
expected_body='{"status":"ok","message":"Joplin Server is running"}'
|
||||
actual_body=$(curl http://localhost:22300/api/ping)
|
||||
|
||||
if [[ "$actual_body" != "$expected_body" ]]; then
|
||||
echo 'Failed while checking the body response after request to /api/ping'
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
- name: Check sync
|
||||
run: |
|
||||
yarn syncFuzzer start --use-running-server --steps 5
|
||||
22
.github/workflows/shared/install-yarn/action.yml
vendored
普通文件
22
.github/workflows/shared/install-yarn/action.yml
vendored
普通文件
@@ -0,0 +1,22 @@
|
||||
name: 'Install Yarn'
|
||||
description: 'Enable Corepack and install the pinned Yarn version, with retries'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install Yarn
|
||||
shell: bash
|
||||
# https://yarnpkg.com/getting-started/install
|
||||
#
|
||||
# `corepack enable` only sets up the shims - Corepack downloads the
|
||||
# pinned Yarn version from repo.yarnpkg.com on first use. That fetch has
|
||||
# a short timeout and no built-in retry, so a transient network blip on
|
||||
# CI fails the whole job. Prepare it eagerly with a retry loop.
|
||||
env:
|
||||
COREPACK_NETWORK_TIMEOUT: '60000'
|
||||
run: |
|
||||
corepack enable
|
||||
for i in 1 2 3; do
|
||||
corepack prepare --activate && break
|
||||
echo "corepack prepare failed (attempt $i), retrying in 15s..."
|
||||
sleep 15
|
||||
done
|
||||
72
.github/workflows/shared/setup-build-environment/action.yml
vendored
普通文件
72
.github/workflows/shared/setup-build-environment/action.yml
vendored
普通文件
@@ -0,0 +1,72 @@
|
||||
name: 'Setup build environment'
|
||||
description: 'Install Joplin build dependencies'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
# Trying to fix random networking issues on Windows
|
||||
# https://github.com/actions/runner-images/issues/1187#issuecomment-686735760
|
||||
# - name: Disable TCP/UDP offload on Windows
|
||||
# if: runner.os == 'Windows'
|
||||
# shell: pwsh
|
||||
# run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6
|
||||
|
||||
- name: Disable TCP/UDP offload on Linux
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Disable TCP/UDP offload on macOS
|
||||
if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
sudo sysctl -w net.link.generic.system.hwcksum_tx=0
|
||||
sudo sysctl -w net.link.generic.system.hwcksum_rx=0
|
||||
|
||||
# libsecret-1-dev is required for keytar - https://github.com/atom/node-keytar
|
||||
# xvfb provides a virtual display on Linux, used for Playwright tests.
|
||||
# The apt-get update is allowed to fail (some sources may be temporarily
|
||||
# unreachable, especially on arm64) but the install is retried to absorb
|
||||
# transient mirror failures.
|
||||
|
||||
- name: Install Linux dependencies
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
source "${GITHUB_WORKSPACE}/.github/scripts/retry.sh"
|
||||
sudo apt-get update || true
|
||||
retry 5 15 sudo apt-get install -y \
|
||||
gettext \
|
||||
libsecret-1-dev \
|
||||
translate-toolkit \
|
||||
rsync \
|
||||
xvfb
|
||||
|
||||
- name: Install macOs dependencies
|
||||
if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
# Required for building the canvas package
|
||||
brew install pango
|
||||
|
||||
- uses: olegtarasov/get-tag@v2.1.4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24.15'
|
||||
# Disable the cache on ARM runners. For now, we don't run "yarn install" on these
|
||||
# environments and this breaks actions/setup-node.
|
||||
# See https://github.com/laurent22/joplin/commit/47d0d3eb9e89153a609fb5441344da10904c6308#commitcomment-159577783.
|
||||
# cache: ${{ (!contains(runner.os, 'arm') && 'yarn') || '' }}
|
||||
|
||||
- uses: ./.github/workflows/shared/install-yarn
|
||||
|
||||
# macos-latest ships with Python 3.12 by default, but this removes a
|
||||
# utility that's used by electron-builder (distutils) so we need to pin
|
||||
# Python to an earlier version.
|
||||
# Fixes error `ModuleNotFoundError: No module named 'distutils'`
|
||||
# Ref: https://github.com/nodejs/node-gyp/issues/2869
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.14'
|
||||
65
.github/workflows/ui-tests.yml
vendored
普通文件
65
.github/workflows/ui-tests.yml
vendored
普通文件
@@ -0,0 +1,65 @@
|
||||
name: Joplin UI tests
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
Main:
|
||||
# Don't run on forks
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, windows-2025]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup build environment
|
||||
uses: ./.github/workflows/shared/setup-build-environment
|
||||
- name: Build (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
env:
|
||||
SKIP_ONENOTE_CONVERTER_BUILD: 1
|
||||
run: |
|
||||
. "${env:GITHUB_WORKSPACE}/.github/scripts/build_windows.ps1"
|
||||
Install-WindowsDeps
|
||||
- name: Build
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
source "${GITHUB_WORKSPACE}/.github/scripts/retry.sh"
|
||||
retry yarn install
|
||||
env:
|
||||
# The onenote-converter package uses Rust, which isn't installed on all CI
|
||||
# runners. Since the onenote-converter doesn't have UI tests, it can be excluded
|
||||
# from build:
|
||||
SKIP_ONENOTE_CONVERTER_BUILD: 1
|
||||
- name: Run desktop UI tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${GITHUB_WORKSPACE}/packages/app-desktop/
|
||||
bash ./integration-tests/run-ci.sh
|
||||
# See https://playwright.dev/docs/ci-intro#setting-up-github-actions
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-${{ matrix.os }}
|
||||
path: packages/app-desktop/playwright-report/
|
||||
retention-days: 7
|
||||
Mobile:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: macos-26
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup build environment
|
||||
uses: ./.github/workflows/shared/setup-build-environment
|
||||
- name: Build
|
||||
run: |
|
||||
source "${GITHUB_WORKSPACE}/.github/scripts/retry.sh"
|
||||
retry yarn install
|
||||
env:
|
||||
SKIP_ONENOTE_CONVERTER_BUILD: 1
|
||||
- name: Run mobile UI tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${GITHUB_WORKSPACE}/packages/app-mobile/
|
||||
yarn test-ui-ios
|
||||
在新工单中引用
屏蔽一个用户