Skip to content

gh project item-edit command fails with value for --number is floating point #10342

Closed

Description

@mikekistler

Describe the bug

I'm trying to issue the gh project item-edit command to set the value of an item field to a floating point value. The command looks like this:

gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 69.84 

The command fails with this error:

GraphQL: Number values cannot exceed 8 decimal places (updateProjectV2ItemFieldValue)

which would be helpful if the value I passed had more than 8 decimal places, but it doesn't.

Affected version

Please run gh version and paste the output below.

gh version 2.65.0 (2025-01-06)

Steps to reproduce the behavior

  • Create a project a define a number field in it.
  • Add an item to the project
  • Figure out the project ID, item ID, and field ID (no small feat).
  • Issue the command above.

Expected vs actual behavior

The update should succeed when the value provided is valid.

Logs

Paste the activity from your command line. Redact if needed.

[mikekistler@macbookpro] ~>export GH_DEBUG=true
[mikekistler@macbookpro] ~>gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 69.84

  • Request at 2025-01-30 21:57:28.393059 -0600 CST m=+0.122595376
  • Request to https://api.github.com/graphql
  • Request took 379.058041ms
    GraphQL: Number values cannot exceed 8 decimal places (updateProjectV2ItemFieldValue)
    [mikekistler@macbookpro] ~>

Activity

  1. andyfeller commented on Jan 31, 2025

    @andyfeller
    Contributor

    Thanks for opening up this issue, @mikekistler! 🙇 Instead of GH_DEBUG=true, would you be able to post GH_DEBUG=api results instead?

    My knee jerk is that somehow this number is being manipulated before being sent to the GitHub API, but let's see what ☝ yields.

  2. mikekistler commented on Feb 1, 2025

    @mikekistler
    Author

    @andyfeller That was a good guess!

    [mikekistler@macbookpro] ~>export GH_DEBUG=api
    [mikekistler@macbookpro] ~>gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 69.84
    * Request at 2025-02-01 11:03:20.039991 -0600 CST m=+0.133945001
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token xxxxx
    > Content-Length: 3191
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: America/Chicago
    > User-Agent: GitHub CLI 2.65.0
    
    GraphQL query:
    mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
    GraphQL variables: {"input":{"projectId":"PVT_kwDOAIt-yc4ArJar","itemId":"PVTI_lADOAIt-yc4ArJarzgWylsY","fieldId":"PVTF_lADOAIt-yc4ArJarzgnNyeQ","value":{"number":69.83999633789062}}}
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Sat, 01 Feb 2025 17:03:20 GMT
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes: repo
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
    < X-Github-Request-Id: C8D7:F0029:D1CE19:1A83425:679E53D8
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: gist, project, read:org, repo, workflow
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 4975
    < X-Ratelimit-Reset: 1738429765
    < X-Ratelimit-Resource: graphql
    < X-Ratelimit-Used: 25
    < X-Xss-Protection: 0
    
    {
      "data": {
        "updateProjectV2ItemFieldValue": null
      },
      "errors": [
        {
          "type": "VALIDATION",
          "path": [
            "updateProjectV2ItemFieldValue"
          ],
          "locations": [
            {
              "line": 1,
              "column": 71
            }
          ],
          "message": "Number values cannot exceed 8 decimal places"
        }
      ]
    }
    
    * Request took 301.1675ms
    GraphQL: Number values cannot exceed 8 decimal places (updateProjectV2ItemFieldValue)
    
  3. iamazeem commented on Feb 2, 2025

    @iamazeem
    Contributor

    @andyfeller: Looked into it.
    Apparently, it's related to conversion from float32 to float64.

    From:

    To:

    Number: githubv4.NewFloat(githubv4.Float(config.opts.number)),

    Where, githubv4.Float is graphql.Float:
    https://github.com/shurcooL/githubv4/blob/48295856cce734663ddbd790ff54800f784f3193/scalar.go#L37C2-L37C7

    and, graphql.Float is float64:
    https://github.com/shurcooL/graphql/blob/ed46e5a4646634fc16cb07c3b8db389542cc8847/scalar.go#L18

    Ran this simple test in go playground:

    package main
    
    import "fmt"
    
    func main() {
    	var n float32 = 69.84
    	fmt.Println(n)
    	fmt.Println(float64(n))
    }

    and, the output confirms it:

    69.84
    69.83999633789062

    Given above, fixing the type of number field from float32 to float64 should fix this issue.

  4. andyfeller commented on Feb 5, 2025

    @andyfeller
    Contributor

    Thanks for the follow up, @mikekistler, and the confirmation, @iamazeem!

    Confirmation

    From GraphQL API docs:

    Checking out #10374, I can confirm that this appears to address the gh project item-set issue.

    Reproducing this on private project

    andyfeller@Andys-MBP:cli/cli ‹10342-gh-project-item-edit-number›$ GH_DEBUG=api ./bin/gh project item-edit --project-id PVT_kwDOBWmBn84Al2QA --id PVTI_lADOBWmBn84Al2QAzgXCZ4c --field-id PVTF_lADOBWmBn84Al2QAzgnpmgo --number 69.84
    * Request at 2025-02-05 08:36:37.41416 -0500 EST m=+0.103935210
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token ████████████████████
    > Content-Length: 3191
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: America/New_York
    > User-Agent: GitHub CLI v2.66.1-72-g64b63f61
    
    GraphQL query:
    mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}},iterationId},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
    GraphQL variables: {"input":{"projectId":"PVT_kwDOBWmBn84Al2QA","itemId":"PVTI_lADOBWmBn84Al2QAzgXCZ4c","fieldId":"PVTF_lADOBWmBn84Al2QAzgnpmgo","value":{"number":69.84}}}
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 05 Feb 2025 13:36:38 GMT
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes: repo
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
    < X-Github-Request-Id: CBEE:3A0197:7ACD12:F5F721:67A36965
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: admin:org, admin:public_key, admin:ssh_signing_key, gist, project, repo, workflow
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 5000
    < X-Ratelimit-Reset: 1738765759
    < X-Ratelimit-Resource: graphql
    < X-Ratelimit-Used: 202
    < X-Xss-Protection: 0
    
    {
      "data": {
        "updateProjectV2ItemFieldValue": {
          "projectV2Item": {
            "content": {
              "__typename": "Issue",
              "body": "This is the body",
              "title": "This is a title",
              "number": 37,
              "url": "https://github.com/tinyfists/gh-nonsense-internal/issues/37",
              "repository": {
                "nameWithOwner": "tinyfists/gh-nonsense-internal"
              }
            },
            "id": "PVTI_lADOBWmBn84Al2QAzgXCZ4c",
            "fieldValues": {
              "nodes": [
                {
                  "__typename": "ProjectV2ItemFieldRepositoryValue",
                  "repository": {
                    "url": "https://github.com/tinyfists/gh-nonsense-internal"
                  },
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgd1mYU",
                    "name": "Repository",
                    "dataType": "REPOSITORY"
                  }
                },
                {
                  "__typename": "ProjectV2ItemFieldTextValue",
                  "text": "This is a title",
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgd1mX8",
                    "name": "Title",
                    "dataType": "TITLE"
                  }
                },
                {
                  "__typename": "ProjectV2ItemFieldNumberValue",
                  "number": 69.84,
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgnpmgo",
                    "name": "Floater",
                    "dataType": "NUMBER"
                  }
                }
              ]
            }
          }
        }
      }
    }
    
    * Request took 701.657708ms
    Edited item "This is a title"

    How big of a problem is this?

    Given above, fixing the type of number field from float32 to float64 should fix this issue.

    Looking for other places the GitHub CLI is working with floats seems like there is a little more to this than the proposed changes:

    ProjectV2ItemFieldNumberValue struct {
    Number float32
    Field ProjectField
    } `graphql:"... on ProjectV2ItemFieldNumberValue"`

    I'm working to trace this to the various commands that pull Number fields like this out of the API to see how this 32-bit float comes into play, too.

    if config.opts.exporter != nil {
    return config.opts.exporter.Write(config.io, project.DetailedItems())
    }

    Once I do, then I'll write up acceptance criteria to unblock contributions.

  5. andyfeller commented on Feb 5, 2025

    @andyfeller
    Contributor

    Wrapping up my notes from digging into this a little more before separate recommendations to move this forward.

    Questions

    1. Is there any reason not to change this other reference to 64-bit float?

      As explained below, there is 1 more reference that feels like we should be consistent in making this change.

    2. Should we validate that the --number provided is between 2147483647 and -2147483647?

    As explained below, it appears this isn't really a 64-bit float, so preventing users with argument validation rather than failing API calls seems like the way to go.

    gh project item-list --format json uses 32-bit float

    The only other reference to 32-bit floats in gh project commandset involves exporting project items along with their field values in gh project item-list --format json.

    Though the GraphQL documentation states that Floats are 64-bit double precision, it appears that Number fields for Projects don't exactly measure up. Essentially, Number field values pulled out of the API are roughly 32-bit floats with the following limitations:

    • largest integer value: 2147483647
    • maximum decimal digit precision: 8

    I'm working with the Projects team to update documentation to help Projects users broadly.

    Understanding double float conversions within gh and server side

    While exploring this problem further, I was doing some manual boundary testing to understand how the API endpoints behaved.

    It seems that spf13/pflag or Go is reading flag values and rounding it to 7 decimal places on the client side, which raises the question if we need to ensure gh keeps up to 8 decimal places. 🤔

    UI testing setting a Number field does show users an error when it exceed range, but not when the maximum precision is exceeded.

    Screenshot demonstrating error from maximum integer values exceeded

    Screenshot demonstrating no error from project UI when maximum precision exceeded

    --number 2147483646.987654321 converts to 2147483646.9876542

    andyfeller@Andys-MBP:cli/cli ‹10342-gh-project-item-edit-number›$ GH_DEBUG=api ./bin/gh project item-edit --project-id PVT_kwDOBWmBn84Al2QA --id PVTI_lADOBWmBn84Al2QAzgXCZ4c --field-id PVTF_lADOBWmBn84Al2QAzgnpmgo --number 2147483646.987654321 
    * Request at 2025-02-05 09:14:43.651574 -0500 EST m=+0.077929709
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token ████████████████████
    > Content-Length: 3204
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: America/New_York
    > User-Agent: GitHub CLI v2.66.1-72-g64b63f61
    
    GraphQL query:
    mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}},iterationId},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
    GraphQL variables: {"input":{"projectId":"PVT_kwDOBWmBn84Al2QA","itemId":"PVTI_lADOBWmBn84Al2QAzgXCZ4c","fieldId":"PVTF_lADOBWmBn84Al2QAzgnpmgo","value":{"number":2147483646.9876542}}}
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 05 Feb 2025 14:14:44 GMT
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes: repo
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
    < X-Github-Request-Id: CFBD:2888E6:9F15BC:13EF257:67A37253
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: admin:org, admin:public_key, admin:ssh_signing_key, gist, project, repo, workflow
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 5000
    < X-Ratelimit-Reset: 1738765759
    < X-Ratelimit-Resource: graphql
    < X-Ratelimit-Used: 630
    < X-Xss-Protection: 0
    
    {
      "data": {
        "updateProjectV2ItemFieldValue": {
          "projectV2Item": {
            "content": {
              "__typename": "Issue",
              "body": "This is the body",
              "title": "This is a title",
              "number": 37,
              "url": "https://github.com/tinyfists/gh-nonsense-internal/issues/37",
              "repository": {
                "nameWithOwner": "tinyfists/gh-nonsense-internal"
              }
            },
            "id": "PVTI_lADOBWmBn84Al2QAzgXCZ4c",
            "fieldValues": {
              "nodes": [
                {
                  "__typename": "ProjectV2ItemFieldRepositoryValue",
                  "repository": {
                    "url": "https://github.com/tinyfists/gh-nonsense-internal"
                  },
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgd1mYU",
                    "name": "Repository",
                    "dataType": "REPOSITORY"
                  }
                },
                {
                  "__typename": "ProjectV2ItemFieldTextValue",
                  "text": "This is a title",
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgd1mX8",
                    "name": "Title",
                    "dataType": "TITLE"
                  }
                },
                {
                  "__typename": "ProjectV2ItemFieldNumberValue",
                  "number": 2147483646.9876542,
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgnpmgo",
                    "name": "Floater",
                    "dataType": "NUMBER"
                  }
                }
              ]
            }
          }
        }
      }
    }
    
    * Request took 595.488792ms
    Edited item "This is a title"

    --number 2147483646.1234567890 converts to 2147483646.1234567

    andyfeller@Andys-MBP:cli/cli ‹10342-gh-project-item-edit-number›$ GH_DEBUG=api ./bin/gh project item-edit --project-id PVT_kwDOBWmBn84Al2QA --id PVTI_lADOBWmBn84Al2QAzgXCZ4c --field-id PVTF_lADOBWmBn84Al2QAzgnpmgo --number 2147483646.1234567890
    * Request at 2025-02-05 09:25:10.33057 -0500 EST m=+0.064031876
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token ████████████████████
    > Content-Length: 3204
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: America/New_York
    > User-Agent: GitHub CLI v2.66.1-72-g64b63f61
    
    GraphQL query:
    mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}},iterationId},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
    GraphQL variables: {"input":{"projectId":"PVT_kwDOBWmBn84Al2QA","itemId":"PVTI_lADOBWmBn84Al2QAzgXCZ4c","fieldId":"PVTF_lADOBWmBn84Al2QAzgnpmgo","value":{"number":2147483646.1234567}}}
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 05 Feb 2025 14:25:10 GMT
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes: repo
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
    < X-Github-Request-Id: D060:25126:A8AD1E:1527D9E:67A374C6
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: admin:org, admin:public_key, admin:ssh_signing_key, gist, project, repo, workflow
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 5000
    < X-Ratelimit-Reset: 1738765759
    < X-Ratelimit-Resource: graphql
    < X-Ratelimit-Used: 714
    < X-Xss-Protection: 0
    
    {
      "data": {
        "updateProjectV2ItemFieldValue": {
          "projectV2Item": {
            "content": {
              "__typename": "Issue",
              "body": "This is the body",
              "title": "This is a title",
              "number": 37,
              "url": "https://github.com/tinyfists/gh-nonsense-internal/issues/37",
              "repository": {
                "nameWithOwner": "tinyfists/gh-nonsense-internal"
              }
            },
            "id": "PVTI_lADOBWmBn84Al2QAzgXCZ4c",
            "fieldValues": {
              "nodes": [
                {
                  "__typename": "ProjectV2ItemFieldRepositoryValue",
                  "repository": {
                    "url": "https://github.com/tinyfists/gh-nonsense-internal"
                  },
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgd1mYU",
                    "name": "Repository",
                    "dataType": "REPOSITORY"
                  }
                },
                {
                  "__typename": "ProjectV2ItemFieldTextValue",
                  "text": "This is a title",
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgd1mX8",
                    "name": "Title",
                    "dataType": "TITLE"
                  }
                },
                {
                  "__typename": "ProjectV2ItemFieldNumberValue",
                  "number": 2147483646.1234567,
                  "field": {
                    "__typename": "ProjectV2Field",
                    "id": "PVTF_lADOBWmBn84Al2QAzgnpmgo",
                    "name": "Floater",
                    "dataType": "NUMBER"
                  }
                }
              ]
            }
          }
        }
      }
    }
    
    * Request took 613.7205ms
    Edited item "This is a title"

  6. andyfeller commented on Feb 5, 2025

    @andyfeller
    Contributor

    Acceptance Criteria

    @iamazeem @mikekistler : okay, wrapped up digging into this. I'd like to propose the following AC for to wrap up this issue. I don't think a whole lot of work needs to be done for most of these, but just stating clearly what the expectations once the work is merged.

    1. Given v2 project with a Number field
      When gh project item-edit --number VALUE is called where VALUE <= 2147483647 and VALUE >= -2147483647
      Then VALUE is converted to a 64-bit float according to Go strconv.ParseFloat(..., 64) and the item field is updated.

    2. Given v2 project with a Number field
      And Given project item where this field has been set
      When gh project item-list --format json is called
      Then Number field is return to the user without any loss of precision

    Update

    After working with @BagToad, we're removing the client-side validation AC from this as AC as Projects team is going to improve the server-side validation messaging so we don't need to duplicate it.

  7. added and removed
    more-info-neededMore info needed from user/contributor
    on Feb 5, 2025
  8. andyfeller commented on Feb 5, 2025

    @andyfeller
    Contributor

    @iamazeem : I've assigned the issue to you given your interest; thank you again for your continued support! ❤

  9. iamazeem commented on Feb 6, 2025

    @iamazeem
    Contributor

    @andyfeller: Thank you for the detailed investigation and explanation! 🏆

    Here's another case that I found out for --number that 0 is discarded as an invalid value:

    $ bin/gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 0
    error: no changes to make

    However, given above valid ranges, it is a valid value and can be set from UI:

    Image

    Please update AC accordingly. Thanks!

  10. andyfeller commented on Feb 6, 2025

    @andyfeller
    Contributor

    @iamazeem : have you looked in the history behind that behavior?

    Let's dig into it

    There are a few places in code that checks for --number 0, which harken back to #7375 where gh project commandset was introduced:

    if err := cmdutil.MutuallyExclusive(
    "only one of `--text`, `--number`, `--date`, `--single-select-option-id` or `--iteration-id` may be used",
    opts.text != "",
    opts.number != 0,
    opts.date != "",
    opts.singleSelectOptionID != "",
    opts.iterationID != "",
    ); err != nil {
    return err
    }
    if err := cmdutil.MutuallyExclusive(
    "cannot use `--text`, `--number`, `--date`, `--single-select-option-id` or `--iteration-id` in conjunction with `--clear`",
    opts.text != "" || opts.number != 0 || opts.date != "" || opts.singleSelectOptionID != "" || opts.iterationID != "",
    opts.clear,
    ); err != nil {
    return err
    }

    // update item values
    if config.opts.text != "" || config.opts.number != 0 || config.opts.date != "" || config.opts.singleSelectOptionID != "" || config.opts.iterationID != "" {
    return updateItemValues(config)
    }
    if _, err := fmt.Fprintln(config.io.ErrOut, "error: no changes to make"); err != nil {
    return err
    }

    I suspect the challenge we have here is that 0 is the zero value for 32-bit and 64-bit floats even if the user does not provide --number flag.

    To be clear, this is a problem, however let's proceed with the issue AC we have so far and file a separate issue for this new discovery while I confirm this with original hubber who introduced the commandset and figure out potential workarounds. 🤔

  11. andyfeller commented on Feb 6, 2025

    @andyfeller
    Contributor

    created #10380 for --number 0 and will add notes on potentially how to address it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggh-projectrelating to the gh project commandhelp wantedContributions welcome

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions