Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion .github/workflows/Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ jobs:
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
patchelf \
zsync

- uses: pnpm/action-setup@v4
with:
Expand Down Expand Up @@ -71,6 +72,15 @@ jobs:
CMAKE_POLICY_VERSION_MINIMUM: 3.5
run: node src/celemod-ui/scripts/tauri.mjs build --no-bundle -- --locked

- name: Configure AppImage delta updates
if: runner.os == 'Linux' && (startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master')
run: |
# Tagged builds follow the latest release, master builds follow the nightly release.
# appimagetool (through linuxdeploy) embeds this string and writes the matching .zsync file.
channel=nightly
if [[ "$GITHUB_REF" == refs/tags/* ]]; then channel=latest; fi
echo "UPDATE_INFORMATION=gh-releases-zsync|$GITHUB_REPOSITORY_OWNER|${GITHUB_REPOSITORY#*/}|$channel|CeleMod_*_amd64.AppImage.zsync" >> "$GITHUB_ENV"

- uses: tauri-apps/tauri-action@v0.6.2
if: runner.os != 'Windows'
env:
Expand All @@ -84,6 +94,17 @@ jobs:
releaseDraft: false
prerelease: false

- name: Collect AppImage delta update file
if: runner.os == 'Linux' && (startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master')
run: |
# appimagetool writes the .zsync file into the working directory instead of the bundle directory.
mapfile -t zsync_files < <(find . -maxdepth 2 -name '*.AppImage.zsync')
if [[ ${#zsync_files[@]} -eq 0 ]]; then
echo "::error::appimagetool did not produce an AppImage .zsync file"
exit 1
fi
mv -v "${zsync_files[@]}" target/release/bundle/appimage/

- name: Upload macOS bundles
if: runner.os == 'macOS'
uses: actions/upload-artifact@v4
Expand All @@ -102,6 +123,20 @@ jobs:
target/release/bundle/deb/*.deb
if-no-files-found: error

- name: Upload AppImage delta update file
if: runner.os == 'Linux' && (startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master')
uses: actions/upload-artifact@v4
with:
name: CeleMod-linux-appimage-zsync
path: target/release/bundle/appimage/*.AppImage.zsync
if-no-files-found: error

- name: Upload AppImage delta update to release
if: runner.os == 'Linux' && startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload "$GITHUB_REF_NAME" target/release/bundle/appimage/*.AppImage.zsync --clobber

- name: Rename Windows executable
if: runner.os == 'Windows'
shell: pwsh
Expand Down
2 changes: 1 addition & 1 deletion scripts/publish-nightly.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = async function publishNightly({ github, context, core, director
// Never hide the previous release unless all platforms produced their assets.
const files = collectAssets(directory);
const names = files.map(file => path.basename(file));
for (const suffix of ['.exe', '.dmg', '.AppImage', '.deb', '.apk', '.apk.sha256']) {
for (const suffix of ['.exe', '.dmg', '.AppImage', '.AppImage.zsync', '.deb', '.apk', '.apk.sha256']) {
if (!names.some(name => name.endsWith(suffix))) {
throw new Error(`Missing nightly asset: ${suffix}`);
}
Expand Down
15 changes: 12 additions & 3 deletions scripts/publish-nightly.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const publish = require('./publish-nightly.cjs');
function fixture(t, options = {}) {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'celemod-nightly-'));
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const assets = ['CeleMod.exe', 'CeleMod.dmg', 'CeleMod.AppImage', 'CeleMod.deb', 'CeleMod.apk'];
const assets = [
'CeleMod.exe', 'CeleMod.dmg', 'CeleMod.AppImage', 'CeleMod.AppImage.zsync', 'CeleMod.deb', 'CeleMod.apk',
];
for (const name of assets) fs.writeFileSync(path.join(directory, name), name);
const hash = crypto.createHash('sha256').update('CeleMod.apk').digest('hex');
fs.writeFileSync(path.join(directory, 'CeleMod.apk.sha256'), `${hash} CeleMod.apk\n`);
Expand Down Expand Up @@ -53,7 +55,7 @@ function fixture(t, options = {}) {
test('creates a prerelease and tag only after uploading all platforms', async t => {
const f = fixture(t, { newRelease: true, newTag: true });
await publish(f);
assert.equal(f.calls.filter(c => c.name === 'uploadReleaseAsset').length, 6);
assert.equal(f.calls.filter(c => c.name === 'uploadReleaseAsset').length, 7);
const created = f.calls.find(c => c.name === 'createRelease').args;
assert.equal(created.draft, true);
assert.equal(created.prerelease, true);
Expand All @@ -69,7 +71,7 @@ test('replaces all old assets and moves only the nightly tag', async t => {
await publish(f);
assert.equal(f.calls.find(c => c.name === 'updateRelease').args.draft, true);
assert.deepEqual(f.calls.filter(c => c.name === 'deleteReleaseAsset').map(c => c.args.asset_id), [11, 12]);
assert.equal(f.calls.filter(c => c.name === 'uploadReleaseAsset').length, 6);
assert.equal(f.calls.filter(c => c.name === 'uploadReleaseAsset').length, 7);
const moved = f.calls.find(c => c.name === 'updateRef').args;
assert.equal(moved.ref, 'tags/nightly');
assert.equal(moved.sha, 'commit');
Expand All @@ -91,6 +93,13 @@ test('missing platform assets leave the previous release untouched', async t =>
assert.equal(f.calls.length, 0);
});

test('missing AppImage delta update leaves the previous release untouched', async t => {
const f = fixture(t);
fs.unlinkSync(path.join(f.directory, 'CeleMod.AppImage.zsync'));
await assert.rejects(publish(f), /Missing nightly asset: .AppImage.zsync/);
assert.equal(f.calls.length, 0);
});

test('duplicate basenames from nested artifacts are rejected', async t => {
const f = fixture(t);
fs.mkdirSync(path.join(f.directory, 'nested'));
Expand Down
Loading