Overriding Issue Creation Date when raising a Gitlab Issue

If you're manually migrating existing issues into Gitlab, you may want to override the date that an issue/ticket reports as being raised on.

I wanted to do this recently as GILS's new label timelines functionality means that importing older issues is worthwhile (as it makes it easier to see the full history, even if some of it predates my use of Gitlab).

Unfortunately, Gitlab's UI doesn't provide a means to do this, but the API does.

When I was looking, there weren't any search hits on how to go about changing the creation date of an issue, so this post aims to correct that by detailing the process of filing a Gitlab issue with a custom creation date.


The API

The Issues API allows you to raise issues programatically, however it's important to note that:

  • You will need to authenticate with the token of a user who has admin privileges or is owner of the project/group you're creating the issue in.
  • Once you've got sufficient access, including a ISO 8601 date in created_at will override the creation date.
  • If your privileges are insufficient, you won't get an error back from the API, Gitlab will just ignore created_at.
  • You can only override created_at when creating an issue, it's not possible to change it afterwards as the edit endpoint doesn't support it.

Note: If you're intending to also import issue comments, you can override the date on comments via the Notes API (however, you can't override the author).


Token Creation

If you don't already have a token, you can create one as follows:

  1. Log into your Gitlab account
  2. Click your avatar in the top right
  3. Choose Preferences
  4. Select Access Tokens on the left
  5. Provide a name (and, if you wish, an expiration date)
  6. Tick the api scope
  7. Click Create personal access token

Basic API Request

The syntax for calling the API is fairly straight-forward:

  • It's a POST request
  • You should sumbit form-encoded data
  • The request path is /api/v4/projects/$PROJECT_NAME/issues
  • Where $PROJECT_NAME includes a forward slash, it should be url-encoded (to %2F)

At it's simplest, it can be achieved with curl:

curl -X POST https://$MY_GITLAB/api/v4/projects/jira-projects%2FMISC/issues \
--header "PRIVATE-TOKEN: $MY_TOKEN" \
--data-urlencode "created_at=2021-06-03T18:21:36Z" \
--data-urlencode "title=MISC-46 Dice rolls on random.bentasker.co.uk may not be consistently random" \
--data-urlencode "description=See [MISC-46](https://projects.bentasker.co.uk/jira_projects/browse/MISC-46.html)"   

Giving an issue like this one.


Linking to a Milestone

If issues are associated with a specific version/sprint, you'll probably want to link issues to a specific milestone. Whilst this can be done manually after the fact, there is something to be said for doing this programatically.

Unfortunately, whilst this linking is supported, Gitlab haven't made it easy.

The milestone that the web interface shows is not the ID you need, with the API docs noting:

The global ID of a milestone to assign issue. To find the milestone_id associated with a milestone, view an issue with the milestone assigned and use the API to retrieve the issue’s details.

Basically, you need the internal ID, which isn't displayed in the UI.

Although the API documents suggest assigning an issue to the milestone and then retrieving it, that's often not a very palatable option: in 6 months time, do you want to be trying to remember why that issue was assigned to a milestone and then removed from it?

Thankfully, it's not actually necessary to assign (or make any other changes to) an issue:

  1. Browse to any issue in the project.
  2. Hit F12 to open Developer Tools and switch to the network tab.
  3. Click Edit in the milestone box in Gitlab's UI
  4. In Developer Tools find the POST request to /api/graphql.
  5. Verify that the request body includes operationName: projectMilestones.

If you now expand the response body you'll see some JSON giving the global ID of each of the (open) milestones in the project

Gitlab Global ID

You need the integer from the end of attribute id, so in the screenshot above gid://gitlab/Milestone/32 yields ID 32.

To create an issue in Milestone v0.1, you can place a request like this

curl -X POST https://$MY_GITLAB/api/v4/projects/jira-projects%2FMISC/issues \
--header "PRIVATE-TOKEN: $MY_TOKEN" \
--data-urlencode "created_at=2021-06-03T18:21:36Z" \
--data-urlencode "title=HLS-7 Implement use of getopt" \
--data-urlencode "description=See [HLS-7](https://projects.bentasker.co.uk/jira_projects/browse/HLS-7.html)" \
--data-urlencode "milestone_id=32"

Giving the issue here.


Labels

The API will also allow you to add arbitrary labels to issues. There's no need to pre-create the labels, if the label doesn't already exist, Gitlab will create it:

curl -X POST https://$MY_GITLAB/api/v4/projects/misc%2Ftest_proj/issues \
-v \
--header "PRIVATE-TOKEN: $MY_TOKEN" \
--data-urlencode "created_at=2016-03-11T03:45:40Z" \
--data-urlencode "description=See [foo](http://example.invalid) blah blah foo" \
--data-urlencode "title=Test Issues" \
--data-urlencode "milestone_id=32" \
--data-urlencode "labels=new label,bug,another label"

Conclusion

Having to resort to API calls in order to override a field isn't necessarily the greatest user experience, but needing to override created_at should also be sufficiently rare that Gitlab probably felt it wasn't worth cluttering the UI with.

Once you know how, it's quite easy to raise issues with customised dates, allowing you to import issues into Gitlab, either as a placeholder (like in the examples above) or the full issue.

The API is quite limited in terms of what it'll allow you to override, so if you're importing a full project, you're better off using one of Gitlab's Import Tools if there's one available for your data source.

But, for smaller or selective imports, it's useful to be able to at least ensure that issues are ordered correctly.