Attach ./bin and ./assets to a release
`release` now uploads every file in the project's ./bin and ./assets when
those directories exist. Nothing to configure, and nothing happens for a
project that has neither.
This is the part deliberately left out when `release` was written,
because it is where the three providers stop resembling each other:
Gitea multipart POST to .../releases/<id>/assets?name=<name>
GitHub raw POST to the separate upload host the release object names
in upload_url, whose RFC 6570 template suffix has to go first
GitLab a release stores links, not files: the file goes into the
project's generic package registry and the release gets a
package link pointing at it
So findRelease and createRelease now return a releaseRef carrying the id
and, for GitHub, that upload host -- the id alone cannot address an
upload. Uploads stream from disk rather than buffering: these are whole
binaries, and the Gitea multipart body is assembled through a pipe.
Only regular files directly in those directories are taken. Symlinks are
skipped, which matters here: build.sh leaves bin/mgsh pointing at one of
its siblings, and uploading the same 9M twice under two names helps
nobody. A name present in both directories is used from bin and reported
for assets. Re-releasing a tag replaces same-named assets rather than
failing on them, since rebuilding and publishing again is the normal
reason to do it, and a file that fails does not stop the rest.
Each provider's request shape is pinned down against the recording
stand-in, and the whole chain was run once end to end -- real repository,
real binaries, a fake Gitea that also serves git-http-backend so the tag
push is real too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-11
@@ -9,6 +9,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -20,6 +21,8 @@ import (
|
||||
// recordedReq is one request the fake provider received.
|
||||
type recordedReq struct {
|
||||
method, path, query string
|
||||
ctype string
|
||||
raw []byte // the body as sent, for the asset uploads
|
||||
body map[string]any
|
||||
}
|
||||
|
||||
@@ -42,11 +45,12 @@ func newFakeProvider(t *testing.T) *fakeProvider {
|
||||
body string
|
||||
}{}}
|
||||
f.Server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rec := recordedReq{method: r.Method, path: r.URL.EscapedPath(), query: r.URL.RawQuery}
|
||||
rec := recordedReq{method: r.Method, path: r.URL.EscapedPath(), query: r.URL.RawQuery,
|
||||
ctype: r.Header.Get("Content-Type")}
|
||||
if r.Body != nil {
|
||||
var m map[string]any
|
||||
json.NewDecoder(r.Body).Decode(&m)
|
||||
rec.body = m
|
||||
raw, _ := io.ReadAll(r.Body)
|
||||
rec.raw = raw
|
||||
json.Unmarshal(raw, &rec.body)
|
||||
}
|
||||
f.got = append(f.got, rec)
|
||||
|
||||
@@ -106,7 +110,7 @@ func TestCreateReleasePerProvider(t *testing.T) {
|
||||
f.route("POST "+c.wantPath, 201, `{"id":7}`)
|
||||
|
||||
api := newRemoteAPI(f.URL, "tok", c.typ)
|
||||
if err := api.createRelease("mike", "mgsh", rel); err != nil {
|
||||
if _, err := api.createRelease("mike", "mgsh", rel); err != nil {
|
||||
t.Fatalf("%s: createRelease: %v (requests: %v)", c.typ, err, f.paths())
|
||||
}
|
||||
|
||||
@@ -145,11 +149,11 @@ func TestFindReleaseAndUpdate(t *testing.T) {
|
||||
f.route(c.wantMethod+" "+c.wantUpdate, 200, `{}`)
|
||||
|
||||
api := newRemoteAPI(f.URL, "tok", c.typ)
|
||||
id, found, err := api.findRelease("mike", "mgsh", "v1.2")
|
||||
ref, found, err := api.findRelease("mike", "mgsh", "v1.2")
|
||||
if err != nil || !found {
|
||||
t.Fatalf("%s: findRelease = %q,%v,%v", c.typ, id, found, err)
|
||||
t.Fatalf("%s: findRelease = %+v,%v,%v", c.typ, ref, found, err)
|
||||
}
|
||||
if err := api.updateRelease("mike", "mgsh", id, release{Tag: "v1.2", Name: "v1.2", Body: "new"}); err != nil {
|
||||
if err := api.updateRelease("mike", "mgsh", ref, release{Tag: "v1.2", Name: "v1.2", Body: "new"}); err != nil {
|
||||
t.Fatalf("%s: updateRelease: %v (requests: %v)", c.typ, err, f.paths())
|
||||
}
|
||||
req := f.find(c.wantMethod + " " + c.wantUpdate)
|
||||
@@ -168,9 +172,9 @@ func TestFindReleaseAndUpdate(t *testing.T) {
|
||||
func TestFindReleaseMissing(t *testing.T) {
|
||||
f := newFakeProvider(t) // everything 404s
|
||||
api := newRemoteAPI(f.URL, "tok", "gitea")
|
||||
id, found, err := api.findRelease("mike", "mgsh", "v9")
|
||||
if err != nil || found || id != "" {
|
||||
t.Fatalf("findRelease on empty server = %q,%v,%v", id, found, err)
|
||||
ref, found, err := api.findRelease("mike", "mgsh", "v9")
|
||||
if err != nil || found || ref.id != "" {
|
||||
t.Fatalf("findRelease on empty server = %+v,%v,%v", ref, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user