Validate representative ReleaseKit consumers
This commit is contained in:
@@ -99,9 +99,28 @@ func TestCheckCommandBindsConfiguredChecksToCapturedCandidate(t *testing.T) {
|
|||||||
|
|
||||||
func TestPublishCommandPublishesConfiguredCandidate(t *testing.T) {
|
func TestPublishCommandPublishesConfiguredCandidate(t *testing.T) {
|
||||||
repository := newCLIRepository(t)
|
repository := newCLIRepository(t)
|
||||||
|
writeRepositoryFile(t, repository, "docs/releases/v1.2.3.md", "# Example Product v1.2.3\n\n## Compatibility\n\nNo action required.\n")
|
||||||
|
writeRepositoryFile(t, repository, "scripts/check-release-note.sh", `#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
version=$RELEASEKIT_VERSION
|
||||||
|
candidate=$RELEASEKIT_CANDIDATE
|
||||||
|
test "$(git rev-parse HEAD)" = "$candidate"
|
||||||
|
test "$(sed -n '1p' "docs/releases/$version.md")" = "# Example Product $version"
|
||||||
|
grep -Fxq '## Compatibility' "docs/releases/$version.md"
|
||||||
|
grep -Fxq 'No action required.' "docs/releases/$version.md"
|
||||||
|
grep -Fxq 'module example.com/cli-candidate' go.mod
|
||||||
|
printf '%s %s\n' "$version" "$candidate" >> "$RELEASEKIT_COMPATIBILITY_LOG"
|
||||||
|
`)
|
||||||
|
cliGitRun(t, repository, "add", "docs/releases/v1.2.3.md", "scripts/check-release-note.sh")
|
||||||
|
cliGitRun(t, repository, "commit", "-m", "add compatibility note and check")
|
||||||
|
checkLog := filepath.Join(t.TempDir(), "compatibility-check.log")
|
||||||
settings := cli.Settings{
|
settings := cli.Settings{
|
||||||
Product: "Example Product",
|
Product: "Example Product",
|
||||||
Remote: "origin",
|
Remote: "origin",
|
||||||
|
Checks: []cli.CheckCommand{{
|
||||||
|
Args: []string{"sh", "scripts/check-release-note.sh"},
|
||||||
|
Env: map[string]string{"RELEASEKIT_COMPATIBILITY_LOG": checkLog},
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
contents, err := json.Marshal(settings)
|
contents, err := json.Marshal(settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -133,6 +152,13 @@ func TestPublishCommandPublishesConfiguredCandidate(t *testing.T) {
|
|||||||
if got := cliGitOutput(t, remote, "rev-parse", "refs/tags/v1.2.3"); got != tagObject {
|
if got := cliGitOutput(t, remote, "rev-parse", "refs/tags/v1.2.3"); got != tagObject {
|
||||||
t.Fatalf("remote tag object = %s, want %s", got, tagObject)
|
t.Fatalf("remote tag object = %s, want %s", got, tagObject)
|
||||||
}
|
}
|
||||||
|
checkResult, err := os.ReadFile(checkLog)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read product compatibility check result: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := strings.Count(string(checkResult), "v1.2.3 "+commit), 1; got != want {
|
||||||
|
t.Fatalf("product compatibility check ran %d times, want %d", got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildCommandWritesValidatedReceiptForConfiguredArtifacts(t *testing.T) {
|
func TestBuildCommandWritesValidatedReceiptForConfiguredArtifacts(t *testing.T) {
|
||||||
|
|||||||
@@ -54,6 +54,51 @@ func TestDeliverManagedBuildUploadsArtifactsAndCleansTemporaryOutput(t *testing.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeliverBinaryConsumerWithBuildinfoAndSelectedTargets(t *testing.T) {
|
||||||
|
repository := newTaggedDeliveryConsumer(t)
|
||||||
|
server, remote := newFacadeDeliveryServer(t, repository)
|
||||||
|
defer server.Close()
|
||||||
|
var progress strings.Builder
|
||||||
|
|
||||||
|
result, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
|
||||||
|
RepositoryDir: repository,
|
||||||
|
Product: "Fixture",
|
||||||
|
Version: "v1.2.3",
|
||||||
|
Destination: facadeDeliveryDestination(server.URL),
|
||||||
|
Build: &releasekit.BuildSpec{
|
||||||
|
MainPackage: "./cmd/app",
|
||||||
|
BinaryName: "fixture",
|
||||||
|
Targets: []releasekit.Target{
|
||||||
|
{OS: "linux", Arch: "amd64"},
|
||||||
|
{OS: "linux", Arch: "arm64"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Output: &progress,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Deliver() error = %v; result = %#v", err, result)
|
||||||
|
}
|
||||||
|
if result.State != releasekit.DeliveryComplete || result.CandidateCommit != remote.commit {
|
||||||
|
t.Fatalf("delivery result = %#v", result)
|
||||||
|
}
|
||||||
|
wantAssets := []string{
|
||||||
|
"fixture-v1.2.3-linux-amd64",
|
||||||
|
"fixture-v1.2.3-linux-arm64",
|
||||||
|
"SHA256SUMS",
|
||||||
|
}
|
||||||
|
if len(result.Uploaded) != len(wantAssets) || len(remote.assets) != len(wantAssets) {
|
||||||
|
t.Fatalf("uploaded = %#v; hosted assets = %v", result.Uploaded, mapKeys(remote.assets))
|
||||||
|
}
|
||||||
|
for _, name := range wantAssets {
|
||||||
|
if _, ok := remote.assets[name]; !ok {
|
||||||
|
t.Errorf("hosted assets omit %q: %v", name, mapKeys(remote.assets))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !strings.Contains(progress.String(), "checking fixture version output") {
|
||||||
|
t.Fatalf("build progress = %q", progress.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeliverPrebuiltArtifactsRemainCallerOwned(t *testing.T) {
|
func TestDeliverPrebuiltArtifactsRemainCallerOwned(t *testing.T) {
|
||||||
repository := newTaggedDeliveryConsumer(t)
|
repository := newTaggedDeliveryConsumer(t)
|
||||||
outputDir := filepath.Join(t.TempDir(), "caller-owned-assets")
|
outputDir := filepath.Join(t.TempDir(), "caller-owned-assets")
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ selected build targets; source-only delivery does not need application
|
|||||||
dependencies. This preparation may access the network and may update
|
dependencies. This preparation may access the network and may update
|
||||||
`go.sum`. The command takes CI values as arguments, so profile settings remain
|
`go.sum`. The command takes CI values as arguments, so profile settings remain
|
||||||
independent of Woodpecker environment variable names. See the
|
independent of Woodpecker environment variable names. See the
|
||||||
[CI command contract](cli.md#prepare-a-tagged-ci-checkout-and-deliver-it) for
|
[CLI contract](cli.md) for the invocation and exit statuses.
|
||||||
the invocation and exit statuses.
|
|
||||||
|
|
||||||
ReleaseKit's source-only profile is
|
ReleaseKit's source-only profile is
|
||||||
[releasekit.json](../releasekit.json).
|
[releasekit.json](../releasekit.json).
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ two Go facades and a command-line interface:
|
|||||||
- `buildinfo` is the small runtime facade. `Version` reports the injected
|
- `buildinfo` is the small runtime facade. `Version` reports the injected
|
||||||
application version, the main-module version when available, or `dev`.
|
application version, the main-module version when available, or `dev`.
|
||||||
- `cmd/releasekit` is the executable interface. Its `version`, `check`,
|
- `cmd/releasekit` is the executable interface. Its `version`, `check`,
|
||||||
`publish`, `build`, and `deliver` commands adapt profiles and command output
|
`publish`, `build`, `deliver`, and `ci-deliver` commands adapt profiles and
|
||||||
to the supported library operations.
|
command output to the supported library operations.
|
||||||
|
|
||||||
See the [CLI contract](../cli.md), [configuration reference](../config.md),
|
See the [CLI contract](../cli.md), [configuration reference](../config.md),
|
||||||
[candidate check guide](../consumers/check.md),
|
[candidate check guide](../consumers/check.md),
|
||||||
@@ -66,12 +66,12 @@ not a separate release matrix:
|
|||||||
| Capability | Implementation owner | Existing evidence |
|
| Capability | Implementation owner | Existing evidence |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Runtime version lookup and injected application version | `buildinfo`, `internal/runtimeinfo`, `internal/versioncontract`, `internal/build` | `TestVersionInConsumerExecutable`, `TestVersion` |
|
| Runtime version lookup and injected application version | `buildinfo`, `internal/runtimeinfo`, `internal/versioncontract`, `internal/build` | `TestVersionInConsumerExecutable`, `TestVersion` |
|
||||||
| Candidate capture, standard suite, additional checks, and rechecks | `releasekit.Check`, `releasekit.Publish`, `internal/candidate`, `cmd/releasekit/internal/cli` | `TestCheckCapturesCandidateAndCallsAdditionalChecksOnce`, `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestPublishFailedStandardSuiteSkipsAdditionalChecksAndTagCreation`, `TestPublishAdditionalCheckFailurePreservesCauseAndPreventsTagCreation`, `TestCheckCommandBindsConfiguredChecksToCapturedCandidate` |
|
| Candidate capture, standard suite, additional checks, and rechecks | `releasekit.Check`, `releasekit.Publish`, `internal/candidate`, `cmd/releasekit/internal/cli` | `TestCheckCapturesCandidateAndCallsAdditionalChecksOnce`, `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestPublishFailedStandardSuiteSkipsAdditionalChecksAndTagCreation`, `TestPublishAdditionalCheckFailurePreservesCauseAndPreventsTagCreation`, `TestCheckCommandBindsConfiguredChecksToCapturedCandidate`, `TestPublishCommandPublishesConfiguredCandidate` |
|
||||||
| Shared Go settings for validation and builds | `internal/gotool`, `internal/process`, `internal/candidate`, `internal/build` | `TestDefaultValidationDoesNotInheritGOFLAGSRunFilter`, `TestNativeAndStaticCommandsUseOwnedBaseline`, `TestPersistentSettingsPreserveCachesAndNativeCTools`, `TestBuildIgnoresAmbientAndPersistentArchitectureTuning` |
|
| Shared Go settings for validation and builds | `internal/gotool`, `internal/process`, `internal/candidate`, `internal/build` | `TestDefaultValidationDoesNotInheritGOFLAGSRunFilter`, `TestNativeAndStaticCommandsUseOwnedBaseline`, `TestPersistentSettingsPreserveCachesAndNativeCTools`, `TestBuildIgnoresAmbientAndPersistentArchitectureTuning` |
|
||||||
| Authoritative endpoint and exact annotated tag publication | `releasekit.Publish`, `internal/publish`, `internal/git` | `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestResolveUsesPushURLAndQueriesWithoutMutation` |
|
| Authoritative endpoint and exact annotated tag publication | `releasekit.Publish`, `internal/publish`, `internal/git` | `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestResolveUsesPushURLAndQueriesWithoutMutation` |
|
||||||
| Publication partial and uncertain recovery | `internal/publish`, `internal/git` | `TestPublishRejectedPushKeepsLocalTagAndReportsNotPublished`, `TestPublishLostAcknowledgmentConfirmsExactRemoteObject`, `TestPublishLostAcknowledgmentWithChangedEndpointRemainsUnknown`, `TestPublishUnavailableReconciliationRemainsUnknownAndPreservesPushError` |
|
| Publication partial and uncertain recovery | `internal/publish`, `internal/git` | `TestPublishRejectedPushKeepsLocalTagAndReportsNotPublished`, `TestPublishLostAcknowledgmentConfirmsExactRemoteObject`, `TestPublishLostAcknowledgmentWithChangedEndpointRemainsUnknown`, `TestPublishUnavailableReconciliationRemainsUnknownAndPreservesPushError` |
|
||||||
| Cross-builds, version smoke checks, empty target sets, and partial outputs | `releasekit.Build`, `internal/build`, `buildinfo` | `TestBuildCreatesSelectedTargetsAndInjectsVersion`, `TestBuildWithNoTargetsDoesNotInvokeCompiler`, `TestBuildReturnsPartialArtifactsAfterTargetFailure` |
|
| Cross-builds, version smoke checks, empty target sets, and partial outputs | `releasekit.Build`, `internal/build`, `buildinfo` | `TestBuildCreatesSelectedTargetsAndInjectsVersion`, `TestBuildWithNoTargetsDoesNotInvokeCompiler`, `TestBuildReturnsPartialArtifactsAfterTargetFailure` |
|
||||||
| Page-only and managed or prebuilt binary delivery | `releasekit.Deliver`, `internal/delivery`, `internal/gitea`, CLI | `TestDeliverPageOnlySkipsBuildAndHostedAssets`, `TestDeliverManagedBuildUploadsArtifactsAndCleansTemporaryOutput`, `TestDeliverPrebuiltArtifactsRemainCallerOwned`, `TestDeliverManagedBuildFailureCleansTemporaryOutputBeforeHostedWrites`, `TestDeliverManagedBuildCancellationCleansTemporaryOutput`, `TestDeliverBuildsTaggedArtifactsAndReusesAssets`, `TestDeliverRejectsTamperedAssetsAndStaleReceiptBeforeRemoteWrites` |
|
| Page-only and managed or prebuilt binary delivery | `releasekit.Deliver`, `internal/delivery`, `internal/gitea`, CLI | `TestDeliverPageOnlySkipsBuildAndHostedAssets`, `TestDeliverManagedBuildUploadsArtifactsAndCleansTemporaryOutput`, `TestDeliverBinaryConsumerWithBuildinfoAndSelectedTargets`, `TestDeliverPrebuiltArtifactsRemainCallerOwned`, `TestDeliverManagedBuildFailureCleansTemporaryOutputBeforeHostedWrites`, `TestDeliverManagedBuildCancellationCleansTemporaryOutput`, `TestDeliverBuildsTaggedArtifactsAndReusesAssets`, `TestDeliverRejectsTamperedAssetsAndStaleReceiptBeforeRemoteWrites` |
|
||||||
| Tagged checkout and build dependency preparation | `releasekit.PrepareTaggedCheckout`, `releasekit.PrepareBuildDependencies`, `internal/preparation`, `internal/git`, `internal/gotool` | `TestPrepareTaggedCheckoutFetchesExpectedCommitAndDetaches`, `TestPrepareTaggedCheckoutRejectsWrongCommitBeforeCheckout`, `TestPrepareTaggedCheckoutReportsLocalCheckoutConflict`, `TestPrepareTaggedCheckoutReportsConflictingLocalTag`, `TestPrepareBuildDependenciesPreservesPreparationEnvironment`, `TestPrepareBuildDependenciesPropagatesCommandFailure`, `TestPrepareBuildDependenciesPropagatesCancellation` |
|
| Tagged checkout and build dependency preparation | `releasekit.PrepareTaggedCheckout`, `releasekit.PrepareBuildDependencies`, `internal/preparation`, `internal/git`, `internal/gotool` | `TestPrepareTaggedCheckoutFetchesExpectedCommitAndDetaches`, `TestPrepareTaggedCheckoutRejectsWrongCommitBeforeCheckout`, `TestPrepareTaggedCheckoutReportsLocalCheckoutConflict`, `TestPrepareTaggedCheckoutReportsConflictingLocalTag`, `TestPrepareBuildDependenciesPreservesPreparationEnvironment`, `TestPrepareBuildDependenciesPropagatesCommandFailure`, `TestPrepareBuildDependenciesPropagatesCancellation` |
|
||||||
| Page, manifest, asset comparison, and additive retry outcomes | `internal/gitea` | `TestEnsureReleaseCreatesMissingPageFromCommittedNote`, `TestInspectAssetsDetectsSameNameAndSizeWithDifferentBytes`, `TestUploadMissingPartialFailureThenRerunOnlyMissingAssets` |
|
| Page, manifest, asset comparison, and additive retry outcomes | `internal/gitea` | `TestEnsureReleaseCreatesMissingPageFromCommittedNote`, `TestInspectAssetsDetectsSameNameAndSizeWithDifferentBytes`, `TestUploadMissingPartialFailureThenRerunOnlyMissingAssets` |
|
||||||
| CLI profiles and prepared Woodpecker sequences | `cmd/releasekit`, `cmd/releasekit/internal/cli`, `examples/woodpecker/` | `TestLoadSettingsRejectsInvalidProfiles`, `TestWoodpeckerTagDeliveryTemplate`, `TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout` |
|
| CLI profiles and prepared Woodpecker sequences | `cmd/releasekit`, `cmd/releasekit/internal/cli`, `examples/woodpecker/` | `TestLoadSettingsRejectsInvalidProfiles`, `TestWoodpeckerTagDeliveryTemplate`, `TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout` |
|
||||||
@@ -86,5 +86,7 @@ The [Gitea integration guide](../integrations/gitea.md) owns the HTTP contract
|
|||||||
and hosted response handling. The [Woodpecker examples](../../examples/woodpecker/)
|
and hosted response handling. The [Woodpecker examples](../../examples/woodpecker/)
|
||||||
are prepared integrations; their YAML is not active in this repository, and no
|
are prepared integrations; their YAML is not active in this repository, and no
|
||||||
live Woodpecker runner, configured secret, or hosted delivery has been used.
|
live Woodpecker runner, configured secret, or hosted delivery has been used.
|
||||||
Consumer migrations and measurement of removed consumer infrastructure remain
|
A read-only PromptKit and Narratio migration review and projected deletion
|
||||||
future adoption work.
|
estimates are recorded in the [product roadmap](../roadmap/releasekit.md);
|
||||||
|
actual consumer migrations and measurement of realized savings remain future
|
||||||
|
adoption work.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ReleaseKit audit implementation plan
|
# ReleaseKit audit implementation plan
|
||||||
|
|
||||||
Status: Stages 1–10 complete; Stage 11 next.
|
Status: All implementation stages complete.
|
||||||
Baseline: `e4be7fa` (2026-10-06). The [audit](audit.md) records the evidence,
|
Baseline: `e4be7fa` (2026-10-06). The [audit](audit.md) records the evidence,
|
||||||
product goals, and selected direction. This document owns implementation order
|
product goals, and selected direction. This document owns implementation order
|
||||||
and completion criteria for that work.
|
and completion criteria for that work.
|
||||||
@@ -568,6 +568,27 @@ migration, and remaining deployment work. Every F1–F8 finding has an implement
|
|||||||
resolution and appropriately scoped evidence. Review and commit the final
|
resolution and appropriately scoped evidence. Review and commit the final
|
||||||
changes with a clean worktree.
|
changes with a clean worktree.
|
||||||
|
|
||||||
|
**Completed:** The source-only consumer publication fixture runs one meaningful
|
||||||
|
compatibility check against its committed note and module, then confirms the
|
||||||
|
annotated tag on a local Git remote. A separate binary consumer fixture builds
|
||||||
|
`buildinfo` binaries for selected Linux targets, checks native version output,
|
||||||
|
and delivers both binaries plus `SHA256SUMS` to a local HTTP Gitea fixture.
|
||||||
|
PromptKit and Narratio were reviewed read-only on 2026-10-06; the product
|
||||||
|
roadmap records retained settings, runtime and policy/procedure adaptations,
|
||||||
|
and projected deletion ranges. No sibling repositories were changed.
|
||||||
|
|
||||||
|
The final documentation review reconciled GoDoc, CLI/configuration references,
|
||||||
|
consumer guidance, Woodpecker examples, the internal inventory, and product
|
||||||
|
roadmap. The pre-release API migration is `Validate` to `AdditionalChecks` and
|
||||||
|
`DeliveryRequest.Build` to `*BuildSpec`, with `Prebuilt` for retained artifacts;
|
||||||
|
JSON `checks` remains the ordered additional-check setting. No new product
|
||||||
|
decision was required. The completed stages address F1–F8; real source
|
||||||
|
publication, an immutable tool pin, credentials, workflow activation, and live
|
||||||
|
Gitea/Woodpecker verification remain deployment work.
|
||||||
|
|
||||||
|
The full offline suite and API boundary check pass with Go 1.25.0. Go formatting,
|
||||||
|
documentation whitespace, and changed local-link checks pass.
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
No unresolved product choice is required to implement these stages. The
|
No unresolved product choice is required to implement these stages. The
|
||||||
|
|||||||
@@ -204,6 +204,27 @@ Consumer policy and procedure changes should describe the deliberately adopted
|
|||||||
standard and its compatibility impact. Adoption should demonstrate reduced local
|
standard and its compatibility impact. Adoption should demonstrate reduced local
|
||||||
infrastructure and one coherent release model.
|
infrastructure and one coherent release model.
|
||||||
|
|
||||||
|
## Representative migration review
|
||||||
|
|
||||||
|
Read-only reviews of the available PromptKit and Narratio checkouts were
|
||||||
|
verified on 2026-10-06 at `4b8169d` and `8afb49c`; both worktrees were clean.
|
||||||
|
These are migration assessments, not changes to either consumer.
|
||||||
|
|
||||||
|
| Consumer | Retain in its profile and product checks | Adaptation and procedure changes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PromptKit, source-only | Product identity; its `Summary`, `Compatibility`, `Upgrade`, and `Changes` release-note sections; checks that exercise the three maintained Go examples and their product outputs. No runtime `buildinfo` import or binary targets are needed. | Its current release scripts total 209 lines. The projected deletion is about 120–160 lines of shared publication and generic validation machinery; retain the note-content rule and product-example checks as a configured additional check plus a small wiring test. PromptKit already pushes `main` before publication and creates annotated tags. Its release policy and procedure still need to add the hosted note page, delivery credentials, versioned CI invocation, and separate delivery status and recovery. |
|
||||||
|
| Narratio, binary | Product/module identity, the `./cmd/narratio` main package, `narratio` executable name, Linux amd64, Linux arm64, and macOS arm64 targets, and the `narratio <version>` command. Keep the module-graph check (`go mod tidy -diff`) if it remains useful as a product-specific candidate check. | Its release, check, asset-builder, and helper scripts total 304 lines; two dedicated release test files total 741 lines. Projected deletions are about 220–280 shell lines and 450–650 repeated test lines, leaving a small consumer wiring test and any retained module/note checks. Replace the private linker-version variable with `buildinfo.Version()` while preserving the current command output. Its policy and procedure need to change lightweight tags to annotated tags, state that a successful tag push completes source publication, and move plugin-based page/assets delivery to the shared versioned CI command with content-aware retries and separately provisioned delivery credentials. |
|
||||||
|
|
||||||
|
The deletion figures are estimates from the reviewed files, not measured
|
||||||
|
migration results or a promise that each line will disappear. Product checks,
|
||||||
|
version output, target choices, and at least one ReleaseKit integration check
|
||||||
|
remain with each consumer. The existing cross-project estimate of 1,000–1,250
|
||||||
|
potentially removable lines remains directional until migrations are completed.
|
||||||
|
The self-contained ReleaseKit fixtures exercise a meaningful source-only
|
||||||
|
compatibility check through local Git publication, and a `buildinfo` binary
|
||||||
|
consumer with selected targets through local HTTP delivery. These are separate
|
||||||
|
publication and delivery operations.
|
||||||
|
|
||||||
## Remaining work
|
## Remaining work
|
||||||
|
|
||||||
- Prepare and publish the first ReleaseKit source release under the
|
- Prepare and publish the first ReleaseKit source release under the
|
||||||
|
|||||||
Reference in New Issue
Block a user