Thanks for developing and maintaining hut. This is my first contribution to it. I read the existing code before working on this patch. Suggestions are welcome! This patch adds support for subdirectory flag to pages publish. I split the patch into two parts. One is to update the schema, and the other is to implement the flag. While changing the schema I see that the code generation is adding the comments in a different way than it used to. I am uncertain about the use of String! vs String for subdirectory flag. In this patch, I went with String!. Following the schema, it should be String, but since we pass "/" as the default value of subdirectory (if not provided), String! should be fine too. The only example in favor of String (over String!) I found in the code is in note flag of build job submission. Rest are String!. Dhruvin Gandhi (2): pages: Update the schema pages publish: Add subdirectory flag pages.go | 5 +- srht/pagessrht/gql.go | 54 ++++++++++----- srht/pagessrht/operations.graphql | 4 +- srht/pagessrht/schema.graphqls | 107 ++++++++++++++++++------------ 4 files changed, 106 insertions(+), 64 deletions(-) -- 2.35.1
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~emersion/hut-dev/patches/29739/mbox | git am -3Learn more about email & git
--- srht/pagessrht/gql.go | 49 ++++++++++----- srht/pagessrht/schema.graphqls | 107 +++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 58 deletions(-) diff --git a/srht/pagessrht/gql.go b/srht/pagessrht/gql.go index e3bdb8e..bbabce4 100644 --- a/srht/pagessrht/gql.go +++ b/srht/pagessrht/gql.go @@ -26,10 +26,12 @@ const ( type Cursor string type Entity struct { - Id int32 `json:"id"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - CanonicalName string `json:"canonicalName"` + Id int32 `json:"id"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + // The canonical name of this entity. For users, this is their username + // prefixed with '~'. Additional entity types will be supported in the future. + CanonicalName string `json:"canonicalName"` } type Protocol string @@ -39,17 +41,33 @@ const ( ProtocolGemini Protocol = "GEMINI" ) +// A published website type Site struct { - Id int32 `json:"id"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - Domain string `json:"domain"` - Protocol Protocol `json:"protocol"` - Version string `json:"version"` + Id int32 `json:"id"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + // Domain name the site services + Domain string `json:"domain"` + // The site protocol + Protocol Protocol `json:"protocol"` + // SHA-256 checksum of the source tarball (uncompressed) + Version string `json:"version"` + // Path to the file to serve for 404 Not Found responses + NotFound *string `json:"notFound,omitempty"` +} + +type SiteConfig struct { + // Path to the file to serve for 404 Not Found responses + NotFound *string `json:"notFound,omitempty"` } +// A cursor for enumerating site entries +// +// If there are additional results available, the cursor object may be passed +// back into the same endpoint to retrieve another page. If the cursor is null, +// there are no remaining results to return. type SiteCursor struct { - Results []*Site `json:"results"` + Results []Site `json:"results"` Cursor *Cursor `json:"cursor,omitempty"` } @@ -66,9 +84,12 @@ type User struct { } type Version struct { - Major int32 `json:"major"` - Minor int32 `json:"minor"` - Patch int32 `json:"patch"` + Major int32 `json:"major"` + Minor int32 `json:"minor"` + Patch int32 `json:"patch"` + // If this API version is scheduled for deprecation, this is the date on which + // it will stop working; or null if this API version is not scheduled for + // deprecation. DeprecationDate time.Time `json:"deprecationDate,omitempty"` } diff --git a/srht/pagessrht/schema.graphqls b/srht/pagessrht/schema.graphqls index d2c03c4..90935eb 100644 --- a/srht/pagessrht/schema.graphqls +++ b/srht/pagessrht/schema.graphqls @@ -2,7 +2,7 @@ scalar Cursor scalar Time scalar Upload -# Used to provide a human-friendly description of an access scope +"Used to provide a human-friendly description of an access scope" directive @scopehelp(details: String!) on ENUM_VALUE enum AccessScope { @@ -16,8 +16,10 @@ enum AccessKind { RW @scopehelp(details: "read and write") } -# Decorates fields for which access requires a particular OAuth 2.0 scope with -# read or write access. +""" +Decorates fields for which access requires a particular OAuth 0.0 scope with +read or write access. +""" directive @access(scope: AccessScope!, kind: AccessKind!) on FIELD_DEFINITION enum Protocol { @@ -30,9 +32,11 @@ type Version { major: Int! minor: Int! patch: Int! - # If this API version is scheduled for deprecation, this is the date on which - # it will stop working; or null if this API version is not scheduled for - # deprecation. + """ + If this API version is scheduled for deprecation, this is the date on which + it will stop working; or null if this API version is not scheduled for + deprecation. + """ deprecationDate: Time } @@ -40,8 +44,10 @@ interface Entity { id: Int! created: Time! updated: Time! - # The canonical name of this entity. For users, this is their username - # prefixed with '~'. Additional entity types will be supported in the future. + """ + The canonical name of this entity. For users, this is their username + prefixed with '~'. Additional entity types will be supported in the future. + """ canonicalName: String! } @@ -57,65 +63,78 @@ type User implements Entity { bio: String } -# A published website +"A published website" type Site { id: Int! created: Time! updated: Time! - # Domain name the site services + "Domain name the site services" domain: String! - # The site protocol + "The site protocol" protocol: Protocol! - # SHA-256 checksum of the source tarball (uncompressed) + "SHA-256 checksum of the source tarball (uncompressed)" version: String! + "Path to the file to serve for 404 Not Found responses" + notFound: String } -# A cursor for enumerating site entries -# -# If there are additional results available, the cursor object may be passed -# back into the same endpoint to retrieve another page. If the cursor is null, -# there are no remaining results to return. +""" +A cursor for enumerating site entries + +If there are additional results available, the cursor object may be passed +back into the same endpoint to retrieve another page. If the cursor is null, +there are no remaining results to return. +""" type SiteCursor { - results: [Site]! + results: [Site!]! cursor: Cursor } +input SiteConfig { + "Path to the file to serve for 404 Not Found responses" + notFound: String +} + type Query { - # Returns API version information. + "Returns API version information." version: Version! - # Returns the authenticated user. + "Returns the authenticated user." me: User! @access(scope: PROFILE, kind: RO) - # Returns a list of registered sites on your account. + "Returns a list of registered sites on your account." sites(cursor: Cursor): SiteCursor! @access(scope: SITES, kind: RO) } type Mutation { - # Publishes a website. If the domain already exists on your account, it is - # updated to a new version. If the domain already exists under someone else's - # account, the request is rejected. If the domain does not exist, a new site - # is created. - # - # Every user is given exclusive use over the 'username.srht.site' domain, and - # it requires no special configuration to use. Users may also bring their own - # domain name, in which case they should consult the configuration docs: - # - # https://man.sr.ht/pages.sr.ht - # - # 'content' must be a .tar.gz file. It must contain only directories and - # regular files of mode 644. Symlinks are not supported. No error is returned - # for an invalid tarball; the invalid data is simply discarded. - # - # If protocol is unset, HTTPS is presumed. - # - # If subdirectory is set, only the specified directory is updated. The rest - # of the files are unchanged. + """ + Publishes a website. If the domain already exists on your account, it is + updated to a new version. If the domain already exists under someone else's + account, the request is rejected. If the domain does not exist, a new site + is created. + + Every user is given exclusive use over the 'username.srht.site' domain, and + it requires no special configuration to use. Users may also bring their own + domain name, in which case they should consult the configuration docs: + + https://man.sr.ht/pages.sr.ht + + 'content' must be a .tar.gz file. It must contain only directories and + regular files of mode 644. Symlinks are not supported. No error is returned + for an invalid tarball; the invalid data is simply discarded. + + If protocol is unset, HTTPS is presumed. + + If subdirectory is set, only the specified directory is updated. The rest + of the files are unchanged. + """ publish(domain: String!, content: Upload!, protocol: Protocol, - subdirectory: String): Site! @access(scope: PAGES, kind: RW) + subdirectory: String, siteConfig: SiteConfig): Site! @access(scope: PAGES, kind: RW) + + """ + Deletes a previously published website. - # Deletes a previously published website. - # - # If protocol is unset, HTTPS is presumed. + If protocol is unset, HTTPS is presumed. + """ unpublish(domain: String!, protocol: Protocol): Site @access(scope: SITES, kind: RW) } -- 2.35.1
--- pages.go | 5 +++-- srht/pagessrht/gql.go | 5 +++-- srht/pagessrht/operations.graphql | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pages.go b/pages.go index 36e2e64..59aec1d 100644 --- a/pages.go +++ b/pages.go @@ -26,7 +26,7 @@ func newPagesCommand() *cobra.Command { } func newPagesPublishCommand() *cobra.Command { - var domain, protocol string + var domain, protocol, subdirectory string run := func(cmd *cobra.Command, args []string) { ctx := cmd.Context() @@ -51,7 +51,7 @@ func newPagesPublishCommand() *cobra.Command { file := gqlclient.Upload{Body: f, Filename: filepath.Base(filename)} - site, err := pagessrht.Publish(c.Client, ctx, domain, file, pagesProtocol) + site, err := pagessrht.Publish(c.Client, ctx, domain, file, pagesProtocol, subdirectory) if err != nil { log.Fatalf("failed to publish site: %v", err) } @@ -70,6 +70,7 @@ func newPagesPublishCommand() *cobra.Command { cmd.Flags().StringVarP(&protocol, "protocol", "p", "HTTPS", "protocol (HTTPS or GEMINI)") cmd.RegisterFlagCompletionFunc("protocol", completeProtocol) + cmd.Flags().StringVarP(&subdirectory, "subdirectory", "s", "/", "subdirectory") return cmd } diff --git a/srht/pagessrht/gql.go b/srht/pagessrht/gql.go index bbabce4..d21f6da 100644 --- a/srht/pagessrht/gql.go +++ b/srht/pagessrht/gql.go @@ -93,11 +93,12 @@ type Version struct { DeprecationDate time.Time `json:"deprecationDate,omitempty"` } -func Publish(client *gqlclient.Client, ctx context.Context, domain string, content gqlclient.Upload, protocol Protocol) (publish *Site, err error) { - op := gqlclient.NewOperation("mutation publish ($domain: String!, $content: Upload!, $protocol: Protocol!) {\n\tpublish(domain: $domain, content: $content, protocol: $protocol) {\n\t\tdomain\n\t}\n}\n") +func Publish(client *gqlclient.Client, ctx context.Context, domain string, content gqlclient.Upload, protocol Protocol, subdirectory string) (publish *Site, err error) { + op := gqlclient.NewOperation("mutation publish ($domain: String!, $content: Upload!, $protocol: Protocol!, $subdirectory: String!) {\n\tpublish(domain: $domain, content: $content, protocol: $protocol, subdirectory: $subdirectory) {\n\t\tdomain\n\t}\n}\n") op.Var("domain", domain) op.Var("content", content) op.Var("protocol", protocol) + op.Var("subdirectory", subdirectory) var respData struct { Publish *Site } diff --git a/srht/pagessrht/operations.graphql b/srht/pagessrht/operations.graphql index 3b9652d..2adb2d7 100644 --- a/srht/pagessrht/operations.graphql +++ b/srht/pagessrht/operations.graphql @@ -1,5 +1,5 @@ -mutation publish($domain: String!, $content: Upload!, $protocol: Protocol!) { - publish(domain: $domain, content: $content, protocol: $protocol) { +mutation publish($domain: String!, $content: Upload!, $protocol: Protocol!, $subdirectory: String!) { + publish(domain: $domain, content: $content, protocol: $protocol, subdirectory: $subdirectory) { domain } } -- 2.35.1