mirror of
https://github.com/actualbudget/actual.git
synced 2026-05-10 16:26:43 -05:00
Compare commits
3 Commits
master
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
320d66444a | ||
|
|
17198863a4 | ||
|
|
a9f8ae0e21 |
44
.github/workflows/electron-master.yml
vendored
44
.github/workflows/electron-master.yml
vendored
@@ -117,7 +117,49 @@ jobs:
|
||||
!packages/desktop-electron/dist/Actual-windows.exe
|
||||
packages/desktop-electron/dist/*.AppImage
|
||||
packages/desktop-electron/dist/*.flatpak
|
||||
packages/desktop-electron/dist/*.appx
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.process_version.outputs.version }}
|
||||
|
||||
publish-microsoft-store:
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
environment: release
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
|
||||
steps:
|
||||
- name: Install StoreBroker
|
||||
shell: powershell
|
||||
run: |
|
||||
Install-Module -Name StoreBroker -AcceptLicense -Force -Scope CurrentUser -Verbose
|
||||
|
||||
- name: Download Microsoft Store artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: actual-electron-windows-latest-appx
|
||||
|
||||
- name: Submit to Microsoft Store
|
||||
shell: powershell
|
||||
run: |
|
||||
# Disable telemetry
|
||||
$global:SBDisableTelemetry = $true
|
||||
|
||||
# Authenticate against the store
|
||||
$pass = ConvertTo-SecureString -String '${{ secrets.MICROSOFT_STORE_CLIENT_SECRET }}' -AsPlainText -Force
|
||||
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ${{ secrets.MICROSOFT_STORE_CLIENT_ID }},$pass
|
||||
Set-StoreBrokerAuthentication -TenantId '${{ secrets.MICROSOFT_STORE_TENANT_ID }}' -Credential $cred
|
||||
|
||||
# Zip and create metadata files
|
||||
$artifacts = Get-ChildItem -Path . -Filter *.appx | Select-Object -ExpandProperty FullName
|
||||
New-StoreBrokerConfigFile -Path "$PWD/config.json" -AppId ${{ secrets.MICROSOFT_STORE_PRODUCT_ID }}
|
||||
New-SubmissionPackage -ConfigPath "$PWD/config.json" -DisableAutoPackageNameFormatting -AppxPath $artifacts -OutPath "$PWD" -OutName submission
|
||||
|
||||
# Submit the app
|
||||
# See https://github.com/microsoft/StoreBroker/blob/master/Documentation/USAGE.md#the-easy-way
|
||||
Update-ApplicationSubmission `
|
||||
-AppId ${{ secrets.MICROSOFT_STORE_PRODUCT_ID }} `
|
||||
-SubmissionDataPath "submission.json" `
|
||||
-PackagePath "submission.zip" `
|
||||
-ReplacePackages `
|
||||
-NoStatus `
|
||||
-AutoCommit `
|
||||
-Force
|
||||
|
||||
113
.github/workflows/publish-microsoft-store.yml
vendored
113
.github/workflows/publish-microsoft-store.yml
vendored
@@ -1,113 +0,0 @@
|
||||
name: Publish Microsoft Store
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release tag (e.g. v25.3.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: publish-microsoft-store
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
publish-microsoft-store:
|
||||
runs-on: windows-latest
|
||||
environment: release
|
||||
steps:
|
||||
- name: Resolve version
|
||||
id: resolve_version
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
INPUT_TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
if [[ "$EVENT_NAME" == "release" ]]; then
|
||||
TAG="$RELEASE_TAG"
|
||||
else
|
||||
TAG="$INPUT_TAG"
|
||||
fi
|
||||
|
||||
if [[ -z "$TAG" ]]; then
|
||||
echo "::error::No tag provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate tag format (v-prefixed semver, e.g. v25.3.0 or v1.2.3-beta.1)
|
||||
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
|
||||
echo "::error::Invalid tag format: $TAG (expected v-prefixed semver, e.g. v25.3.0)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="${TAG#v}"
|
||||
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved tag=$TAG version=$VERSION"
|
||||
|
||||
- name: Verify release assets exist
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
STEPS_RESOLVE_VERSION_OUTPUTS_TAG: ${{ steps.resolve_version.outputs.tag }}
|
||||
run: |
|
||||
TAG="${STEPS_RESOLVE_VERSION_OUTPUTS_TAG}"
|
||||
|
||||
echo "Checking release assets for tag $TAG..."
|
||||
ASSETS=$(gh api "repos/${{ github.repository }}/releases/tags/$TAG" --jq '.assets[].name')
|
||||
|
||||
echo "Found assets:"
|
||||
echo "$ASSETS"
|
||||
|
||||
if ! echo "$ASSETS" | grep -q "\.appx$"; then
|
||||
echo "::error::No .appx assets found in release $TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Required .appx assets found."
|
||||
|
||||
- name: Download Microsoft Store artifacts
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
STEPS_RESOLVE_VERSION_OUTPUTS_TAG: ${{ steps.resolve_version.outputs.tag }}
|
||||
run: |
|
||||
TAG="${STEPS_RESOLVE_VERSION_OUTPUTS_TAG}"
|
||||
gh release download "$TAG" --repo "${{ github.repository }}" --pattern "*.appx"
|
||||
|
||||
- name: Install StoreBroker
|
||||
shell: powershell
|
||||
run: |
|
||||
Install-Module -Name StoreBroker -AcceptLicense -Force -Scope CurrentUser -Verbose
|
||||
|
||||
- name: Submit to Microsoft Store
|
||||
shell: powershell
|
||||
run: |
|
||||
# Disable telemetry
|
||||
$global:SBDisableTelemetry = $true
|
||||
|
||||
# Authenticate against the store
|
||||
$pass = ConvertTo-SecureString -String '${{ secrets.MICROSOFT_STORE_CLIENT_SECRET }}' -AsPlainText -Force
|
||||
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ${{ secrets.MICROSOFT_STORE_CLIENT_ID }},$pass
|
||||
Set-StoreBrokerAuthentication -TenantId '${{ secrets.MICROSOFT_STORE_TENANT_ID }}' -Credential $cred
|
||||
|
||||
# Zip and create metadata files
|
||||
$artifacts = Get-ChildItem -Path . -Filter *.appx | Select-Object -ExpandProperty FullName
|
||||
New-StoreBrokerConfigFile -Path "$PWD/config.json" -AppId ${{ secrets.MICROSOFT_STORE_PRODUCT_ID }}
|
||||
New-SubmissionPackage -ConfigPath "$PWD/config.json" -DisableAutoPackageNameFormatting -AppxPath $artifacts -OutPath "$PWD" -OutName submission
|
||||
|
||||
# Submit the app
|
||||
# See https://github.com/microsoft/StoreBroker/blob/master/Documentation/USAGE.md#the-easy-way
|
||||
Update-ApplicationSubmission `
|
||||
-AppId ${{ secrets.MICROSOFT_STORE_PRODUCT_ID }} `
|
||||
-SubmissionDataPath "submission.json" `
|
||||
-PackagePath "submission.zip" `
|
||||
-ReplacePackages `
|
||||
-NoStatus `
|
||||
-AutoCommit `
|
||||
-Force
|
||||
18
.github/workflows/vrt-update-generate.yml
vendored
18
.github/workflows/vrt-update-generate.yml
vendored
@@ -82,17 +82,16 @@ jobs:
|
||||
with:
|
||||
download-translations: 'false'
|
||||
- name: Build browser bundle
|
||||
# REACT_APP_NETLIFY=true flips isNonProductionEnvironment() on in the
|
||||
# bundle so the "Create test file" button (used by every e2e beforeEach
|
||||
# via ConfigurationPage.createTestFile()) is still rendered in a
|
||||
# production build. Without it, e2e tests would time out waiting for
|
||||
# a button that was tree-shaken out.
|
||||
# --skip-translations keeps VRT screenshots deterministic by rendering
|
||||
# source-code English instead of upstream Weblate en.json (which can
|
||||
# drift between snapshot capture and test runs).
|
||||
# REACT_APP_NETLIFY=true keeps the "Create test file" button in the
|
||||
# production bundle — every VRT test's beforeEach relies on it via
|
||||
# ConfigurationPage.createTestFile().
|
||||
env:
|
||||
REACT_APP_NETLIFY: 'true'
|
||||
run: yarn build:browser --skip-translations
|
||||
run: |
|
||||
yarn workspace plugins-service build
|
||||
yarn workspace @actual-app/crdt build
|
||||
yarn workspace @actual-app/core build:browser
|
||||
yarn workspace @actual-app/web build:browser
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
@@ -258,7 +257,6 @@ jobs:
|
||||
|
||||
- name: Merge shard patches
|
||||
id: create-patch
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
|
||||
@@ -590,6 +590,8 @@ export function useSyncAccountsMutation() {
|
||||
accountIdsToSync = accountIdsToSync.filter(
|
||||
id => !simpleFinAccounts.find(sfa => sfa.id === id),
|
||||
);
|
||||
|
||||
dispatch(setAccountsSyncing({ ids: accountIdsToSync }));
|
||||
}
|
||||
|
||||
// Loop through the accounts and perform sync operation.. one by one
|
||||
|
||||
@@ -466,6 +466,7 @@ const AccountList = forwardRef<HTMLDivElement, AccountListProps>(
|
||||
<ListBox
|
||||
aria-label={ariaLabel}
|
||||
items={accounts}
|
||||
dependencies={[syncingAccountIds, failedAccounts, updatedAccounts]}
|
||||
dragAndDropHooks={dragAndDropHooks}
|
||||
ref={ref}
|
||||
style={{
|
||||
|
||||
@@ -190,11 +190,9 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
|
||||
buttonProps,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onChangeValue,
|
||||
...props
|
||||
}: FocusableAmountInputProps) {
|
||||
const [isNegative, setIsNegative] = useState(true);
|
||||
const [liveValue, setLiveValue] = useState(Math.abs(value));
|
||||
|
||||
const maybeApplyNegative = (amount: number, negative: boolean) => {
|
||||
const absValue = Math.abs(amount);
|
||||
@@ -205,15 +203,6 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
|
||||
props.onUpdateAmount?.(maybeApplyNegative(amount, negative));
|
||||
};
|
||||
|
||||
const handleChangeValue = (text: string) => {
|
||||
setLiveValue(currencyToAmount(text) || 0);
|
||||
onChangeValue?.(text);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLiveValue(Math.abs(value));
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sign) {
|
||||
setIsNegative(sign === '-');
|
||||
@@ -238,11 +227,10 @@ export const FocusableAmountInput = memo(function FocusableAmountInput({
|
||||
value={value}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onChangeValue={handleChangeValue}
|
||||
onUpdateAmount={amount => onUpdateAmount(amount, isNegative)}
|
||||
focused={focused && !disabled}
|
||||
style={{
|
||||
...makeAmountFullStyle(maybeApplyNegative(liveValue, isNegative), {
|
||||
...makeAmountFullStyle(value, {
|
||||
zeroColor: isNegative ? theme.numberNegative : theme.numberNeutral,
|
||||
positiveColor: theme.numberPositive,
|
||||
negativeColor: theme.numberNegative,
|
||||
|
||||
@@ -177,12 +177,15 @@ app.use('/', async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const { method = 'GET', headers: customHeaders = {} } = req.body || {};
|
||||
// Extract method, body, and headers from the request body (sent by loot-core)
|
||||
const {
|
||||
method = 'GET',
|
||||
body,
|
||||
headers: customHeaders = {},
|
||||
} = req.body || {};
|
||||
|
||||
if (typeof method !== 'string') {
|
||||
return res.status(400).json({ error: 'Invalid method parameter' });
|
||||
}
|
||||
const methodNormalized = method.toUpperCase();
|
||||
const methodNormalized =
|
||||
typeof method === 'string' ? method.toUpperCase() : 'GET';
|
||||
if (!['GET', 'HEAD'].includes(methodNormalized)) {
|
||||
return res.status(405).json({ error: 'Method not allowed' });
|
||||
}
|
||||
@@ -215,8 +218,13 @@ app.use('/', async (req, res) => {
|
||||
}
|
||||
|
||||
const response = await fetch(url.href, {
|
||||
method: methodNormalized,
|
||||
method,
|
||||
headers: requestHeaders,
|
||||
body: ['GET', 'HEAD'].includes(method)
|
||||
? undefined
|
||||
: typeof body === 'string'
|
||||
? body
|
||||
: JSON.stringify(body),
|
||||
});
|
||||
|
||||
const contentType =
|
||||
|
||||
@@ -414,55 +414,6 @@ describe('app-cors-proxy', () => {
|
||||
expect(res.statusCode).toBe(405);
|
||||
expect(res.body.error).toBe('Method not allowed');
|
||||
});
|
||||
|
||||
it('should reject non-string method (array bypass)', async () => {
|
||||
global.fetch.mockClear();
|
||||
|
||||
const res = await request(app)
|
||||
.get('/')
|
||||
.send({ method: ['POST'], body: { evil: true } })
|
||||
.query({ url: 'https://api.github.com/repos/user/repo1' });
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body.error).toBe('Invalid method parameter');
|
||||
|
||||
const proxyCalls = global.fetch.mock.calls.filter(
|
||||
([url]) => url === 'https://api.github.com/repos/user/repo1',
|
||||
);
|
||||
expect(proxyCalls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should reject non-string method (object bypass)', async () => {
|
||||
global.fetch.mockClear();
|
||||
|
||||
const res = await request(app)
|
||||
.get('/')
|
||||
.send({ method: { toString: () => 'POST' } })
|
||||
.query({ url: 'https://github.com/user/repo1' });
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body.error).toBe('Invalid method parameter');
|
||||
|
||||
const proxyCalls = global.fetch.mock.calls.filter(
|
||||
([url]) => url === 'https://github.com/user/repo1',
|
||||
);
|
||||
expect(proxyCalls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should forward the validated method to fetch, not the raw input', async () => {
|
||||
global.fetch.mockClear();
|
||||
|
||||
await request(app)
|
||||
.get('/')
|
||||
.send({ method: 'get' })
|
||||
.query({ url: 'https://github.com/user/repo1' });
|
||||
|
||||
const proxyCall = global.fetch.mock.calls.find(
|
||||
([url]) => url === 'https://github.com/user/repo1',
|
||||
);
|
||||
expect(proxyCall).toBeDefined();
|
||||
expect(proxyCall[1].method).toBe('GET');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GitHub authentication', () => {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MikesGlitch]
|
||||
---
|
||||
|
||||
Alter desktop app publish workflow to publish to Microsoft store after release is published
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: Bugfix
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Mobile: add live value tracking for user input in mobile transactions.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Fix update-vrt workflow
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Fix /update-vrt merge step failing on Playwright container with `shopt: not found`
|
||||
6
upcoming-release-notes/7784.md
Normal file
6
upcoming-release-notes/7784.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Bugfixes
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Fix mobile bank sync indicators not updating live during sync.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: Bugfixes
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Fix an issue where the CORS proxy could be bypassed.
|
||||
Reference in New Issue
Block a user