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
3 changes: 3 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:

steps:
- uses: actions/checkout@v7
with:
# GitVersion computes the version from tags and history, and fails on a shallow clone
fetch-depth: 0

- name: Cleanup previous test services
if: matrix.os == 'ubuntu-latest'
Expand Down
43 changes: 23 additions & 20 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
# Packs the NuGet package on a version tag and publishes it to nuget.org.
# Releases are cut by pushing a `v*` tag; the package version is derived from that tag (v1.2.3 -> 1.2.3)
# and injected via `-p:Version`. This workflow only runs for those tags.
# GitVersion (see GitVersion.yml) is in ManualDeployment mode, so releases are cut by pushing a
# `v*` tag, and the package version is resolved from that tag (v1.2.3 -> 1.2.3). This workflow only
# runs for those tags.
#
# It runs in two stages:
# 1. build-test - resolves the version from the tag, then builds, tests and packs, and prints the
# exact .nupkg name to the run summary. Integration tests that need live services
# auto-skip on windows-latest (no service env vars set), so this stage runs the
# unit-test suite only.
# 2. publish - gated behind the `nuget` environment: the run pauses for a manual approval
# before anything is pushed to NuGet or a GitHub release is cut.
# It runs in two stages, both automatic - pushing the tag is the only human action:
# 1. build-test - builds, tests and packs, then prints the exact .nupkg name (with its
# GitVersion-resolved version) to the run summary and uploads it as an artifact.
# Integration tests that need live services auto-skip on windows-latest (no service
# env vars set), so this stage runs the unit-test suite only.
# 2. publish - downloads that same artifact, pushes it to nuget.org via Trusted Publishing
# and cuts the GitHub release.
#
# The `nuget` environment is still declared, and must stay: the nuget.org trusted-publishing policy
# pins it, so the OIDC exchange only works from a job that enters it. It carries no required
# reviewers, so it doesn't pause the run; its remaining protection is the environment's
# "Deployment branches and tags" rule, which restricts it to `v*` tags.
#
# nuget.org versions are immutable: once this workflow pushes, that version is permanent.
# The tag is the point of no return.

name: Release

Expand All @@ -28,25 +37,18 @@ jobs:
uses: actions/setup-dotnet@v6
with:
dotnet-version: 8.0.x
- name: Resolve version from tag
id: version
run: |
# v1.2.3 -> 1.2.3, v1.2.3-beta.1 -> 1.2.3-beta.1 (a valid NuGet pre-release version).
$version = $env:GITHUB_REF_NAME -replace '^[vV]', ''
"version=$version" | Out-File $env:GITHUB_OUTPUT -Append
Write-Host "Release version: $version"
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore -c Release -p:Version=${{ steps.version.outputs.version }}
run: dotnet build --no-restore -c Release
- name: Test
run: dotnet test --no-build -c Release --verbosity normal
- name: Pack
run: dotnet pack src/SharpSync/SharpSync.csproj --no-build -c Release -o artifacts -p:Version=${{ steps.version.outputs.version }}
run: dotnet pack src/SharpSync/SharpSync.csproj --no-build -c Release -o artifacts
- name: Show packed package
run: |
$packages = Get-ChildItem artifacts/*.nupkg
"### 📦 Package awaiting approval" | Out-File $env:GITHUB_STEP_SUMMARY -Append
"### 📦 Packed package" | Out-File $env:GITHUB_STEP_SUMMARY -Append
foreach ($p in $packages) {
$size = '{0:N1} KB' -f ($p.Length / 1KB)
"- **$($p.Name)** ($size)" | Out-File $env:GITHUB_STEP_SUMMARY -Append
Expand Down Expand Up @@ -80,7 +82,8 @@ jobs:
path: artifacts
# Trusted Publishing: exchange the GitHub OIDC token for a short-lived (1 hour, single-use)
# nuget.org API key. Requires a matching trusted-publishing policy on nuget.org bound to this
# repo, the `release.yml` workflow file and the `nuget` environment. No stored API key.
# repo, the `release.yml` workflow file and the `nuget` environment (hence the `environment:`
# key above - dropping it would break the OIDC exchange). No stored API key.
- name: NuGet login (OIDC to temporary API key)
uses: NuGet/login@v1
id: nuget-login
Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.0.4] - 2026-09-10

### Fixed

- `SynchronizeAsync`, `SyncFolderAsync`, and `SyncFilesAsync` called without `SyncOptions` ignored the documented option defaults, so `PreserveTimestamps` and `PreservePermissions` were effectively off. The engine now falls back to a default `SyncOptions` instance. Besides losing timestamps, this could make a later sync treat an unchanged file as modified and raise a spurious conflict.

### Changed

- Updated dependencies: `SSH.NET` 2026.0.0, `AWSSDK.S3` 4.0.103.1, `Microsoft.Extensions.Logging.Abstractions` 10.0.12.
- Removed the explicit `SQLitePCLRaw.bundle_e_sqlite3` reference again. A stale dependency update had re-added it after 1.0.3, and it pulled in a second native SQLite package (`SQLite` 3.53.4) whose files collide with the `SourceGear.sqlite3` library that `sqlite-net-pcl` already provides.

## [1.0.3] - 2026-07-17

### Security
Expand Down Expand Up @@ -99,5 +104,8 @@ A pure .NET 8.0 file synchronization library. SharpSync provides a modular, inte
- Multi-platform CI/CD (Ubuntu, Windows, macOS) with integration tests on Ubuntu via Docker
- Console sample application with OAuth2 example

[1.0.4]: https://github.com/Oire/sharp-sync/compare/v1.0.3...v1.0.4
[1.0.3]: https://github.com/Oire/sharp-sync/compare/v1.0.2...v1.0.3
[1.0.2]: https://github.com/Oire/sharp-sync/compare/v1.0.0...v1.0.2
[1.0.1]: https://github.com/Oire/sharp-sync/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/Oire/sharp-sync/releases/tag/v1.0.0
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@ See [TESTING.md](TESTING.md) for detailed testing documentation.

### Creating NuGet Package
```bash
# Create NuGet package
# Create NuGet package (GitVersion sets the version; untagged commits get a pre-release version)
dotnet pack --configuration Release

# Pack specific project with output directory
dotnet pack src/SharpSync/SharpSync.csproj --configuration Release --output ./artifacts

# Pack with version suffix
dotnet pack --configuration Release --version-suffix preview
```

### Releasing
The package version is derived from git tags by `GitVersion.MsBuild` (`GitVersion.yml`, `ManualDeployment` mode); there is no `<Version>` in the csproj. To release, update `CHANGELOG.md` on master first, then push an annotated `v*` tag (e.g. `v1.0.5`). `.github/workflows/release.yml` then builds, tests, packs, publishes to nuget.org via Trusted Publishing and creates the GitHub release, with no approval step: the `nuget` environment only restricts deployments to `v*` tags. nuget.org versions are immutable, so pushing the tag is the point of no return.

### CI/CD Pipeline Commands
The project uses GitHub Actions for CI/CD. The pipeline currently:
- Builds and tests on **Ubuntu, Windows, and macOS** (matrix strategy)
Expand Down Expand Up @@ -162,13 +162,13 @@ SharpSync is a **pure .NET file synchronization library** with no native depende
### Dependencies

- `Microsoft.Extensions.Logging.Abstractions` - Logging abstraction
- `sqlite-net-pcl` / `SQLitePCLRaw.bundle_e_sqlite3` - SQLite database
- `sqlite-net-pcl` - SQLite database. It brings its own `SQLitePCLRaw` provider and the `SourceGear.sqlite3` native library for every platform; don't add `SQLitePCLRaw.bundle_e_sqlite3` on top (it pulls a second, colliding native SQLite package)
- `WebDav.Client` - WebDAV protocol
- `SSH.NET` - SFTP protocol implementation
- `FluentFTP` - FTP/FTPS protocol implementation
- `AWSSDK.S3` - Amazon S3 and S3-compatible storage
- `Roslynator.Analyzers` - Code quality analyzers (dev-only, `PrivateAssets="all"`)
- `Microsoft.SourceLink.GitHub` - SourceLink for NuGet debugging (dev-only, `PrivateAssets="all"`)
- `GitVersion.MsBuild` - Derives the package version from git tags (dev-only, `PrivateAssets="all"`)
- Target Framework: .NET 8.0
- Deterministic builds enabled, embedded debug symbols, SourceLink configured

Expand Down
8 changes: 8 additions & 0 deletions GitVersion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mode: ManualDeployment
tag-prefix: '[vV]'
assembly-versioning-format: '{Major}.{Minor}.{Patch}.{VersionSourceDistance}'
assembly-file-versioning-format: '{Major}.{Minor}.{Patch}.{VersionSourceDistance}'
assembly-informational-format: '{Major}.{Minor}.{Patch}.{VersionSourceDistance}'
branches:
main:
increment: None
12 changes: 8 additions & 4 deletions src/SharpSync/SharpSync.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
<RepositoryUrl>https://github.com/Oire/sharp-sync</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>file-sync;synchronization;webdav;sftp;ftp;ftps;s3;aws;nextcloud;owncloud;backup;sync</PackageTags>
<!-- No <Version> here: releases are cut by pushing a `v*` tag, and release.yml injects the version
via `-p:Version` (derived from the tag) at build/pack time. Local builds default to 1.0.0, which
is fine because only the tagged CI build is ever published. -->
<!-- No <Version> here: GitVersion.MsBuild derives it from git tags (see GitVersion.yml, ManualDeployment
mode). A release is cut by pushing a `v*` tag; untagged builds get a pre-release version. -->
<PackageReleaseNotes>https://github.com/Oire/sharp-sync/blob/master/CHANGELOG.md</PackageReleaseNotes>

<!-- Reproducible build + source debugging. SourceLink is bundled in the .NET 8+ SDK, so no
Expand Down Expand Up @@ -61,8 +60,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="GitVersion.MsBuild" Version="6.8.2">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<!-- sqlite-net-pcl brings its own SQLitePCLRaw provider and the SourceGear.sqlite3 native library for
every platform. Don't add SQLitePCLRaw.bundle_e_sqlite3 on top: it pulls a second native SQLite
package whose files have the same names, and whichever copy wins then depends on build order. -->
<PackageReference Include="sqlite-net-pcl" Version="1.11.285" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
<PackageReference Include="WebDav.Client" Version="2.9.0" />
<PackageReference Include="SSH.NET" Version="2026.0.0" />
<PackageReference Include="AWSSDK.S3" Version="4.0.103.1" />
Expand Down