This is about migrating our data and workflow from Redmine to GitLab,
which is tracked as [[!tails_ticket 15878]].
# Issues tracking
See also the [GitLab doc on issues](https://docs.gitlab.com/ce/user/project/issues/).
## Private issues
One can make an issue
when creating it; confidentiality can later be toggled on/off at any
time. A confidential issue is visible only by whoever created it and
by project members with at least
Given "Reporter" access is needed for lots of relatively basic
operations, such as labelling and assigning issues, presumably most
active Tails contributors would get at least this access level on the
`tails-team/tails` project, and then in turn access to this project's
confidential issues. So Tails teams that need to further restrict
access to their confidential issues will need to track their issues
under their own `tails-team/$project`.
On Redmine we heavily use relationships between fine-grained issues:
parent/subtasks, Related, Blocks, etc. There's no such thing in
GitLab FOSS ("CE") edition so we'll need to significantly change
our workflow here.
Below we describe potential solutions.
### Parent/subtask and Blocks relationship
A GitLab issue can have a list of tasks (checklist).
GitLab displays "X of Y tasks completed" prominently.
Describing each subtask in free-form text, directly as an item on this
list of tasks, should work fine to emulate a set of subtasks that are
all assigned to the same person. For example, see [[!gnome_gitlab
For more complex cases, e.g. when non-trivial subtasks are done by
different people (GitLab CE does not support multiple assignees per
issue), each subtask could be an item on this list of tasks, that
links to another issue. The downside is that after resolving one of
the subtasks, one will also need to mark the corresponding item as
completed on the task list of the "parent" issue.
This should also replace most of our current usage of the Blocks
Alternatively, one can add a comment with a list of the blocker
issues. Comments can be updated and links will indicate which of the
blockers are closed (strike-through).
### Related to
We can write this information directly either in the description of
one of the issues that are related to each other or in a comment.
Either way, this adds a message in the Activity stream of the
referenced issue, with a link to the other issue. Granted, on issues
with one single long discussion thread, as we're used to on Redmine,
such messages will be lost, so better cross-reference issues in the
description of each related issue, or use labels, or get used to
"Start Discussion" to separate multiple sub-threads that can be marked
as resolved independently from each other.
We can close duplicates with a comment that references the duplicated
issue. It adds a message to the Activity stream of the referenced
issue, which allows one to find duplicates later on if needed.
And to ensure we can list issues that have really been resolved,
add a "Duplicate" label.
## Other issues metadata
- Target version → Milestone
- Feature branch: GitLab will automatically link a branch that mentions
- Category, Affected Tool, Priority, Type of work → use a set of
labels, each with a prefix, for each of them; and maybe simplify
a bit. For example:
- Qubes uses "P: $priority", "C: $category", etc.
- One can set multiple labels so we could perhaps merge "Category"
and "Affected Tool". For example, a ticket about Thunderbird
persistence could have the two "C: email" and "C: persistence" labels.
- Status: use a set of labels, each with a numerical prefix, because
it's an ordered flow:
- "0. Needs triage" (ideally, have it set automatically on newly
created issues; probably requires a webhook; otherwise, can be
done in batch regularly on all issues that have no status label
- "1. Backlog" ("Confirmed")
- "2. Working on it" (clearer than the too vague "In progress")
- "3. To review" (previously "Ready for QA")
- "4. To release" (i.e. closed issue but code not released yet,
to replace "Fix committed" which is too often misunderstood)
- Status = Resolved → closed with neither "Rejected" nor "Duplicate" label
- Status = Duplicate → closed with "Duplicate" laben
- Status = Rejected → closed with "Rejected" label
- Log time → Time tracking
- Due date → Due date
- Starter → dedicated label
- Tracker: drop it (we've never really taken advantage of the fine
distinction between describing a problem as a bug vs. describing
its solution as a feature)
- % Done: drop it (we don't use this field enough to provide any value)
- Estimated time → description
- Attachments: any issue/MR description or comment can have file
## Project management
Currently we use the Redmine "Deliverable for" custom field.
A "Deliverable for SponsorX" label should do the job for tracking
global progress on a grant: then one can use the issues list or an
issues board to visualize progress.
An issues list can be ordered by due date, milestone due date, etc.,
which should emulate the most important aspects of the Redmine custom
queries we use, except naming the query (see "Custom queries" below).
We can track progress on a specific project (be it a grant deliverable
or not) by adding another label, e.g. "Project XYZ". For example,
lists issues that have "Deliverable for" = "SponsorX" and whose parent
task contains [[!tails_ticket 14568]]. We would have labels
"Deliverable for SponsorX" and "Project XYZ". And if an additional
intermediary level of tracking is needed between "this is part of
SponsorX" and "this is about Additional Software" we can create
tracking issues that each have list of tasks.
## Personal self-management
For planning, the same solutions as for project management apply,
modulo adding a filter for the assignee.
And for more lightweight tracking, GitLab's Todos are great. Todos are
fully orthogonal to "tickets assigned to me"; they are listed on
a different page and can be marked as done with one single click.
- Every time one mentions me somewhere, I get a Todo item.
This allows me to track the questions I've been asked, that is, to
replace our usage of "Info Needed", without the need to reassign an
issue or to create a subtask.
- I can click "Add todo" on an issue and it will appear on my list
of Todos (regardless of whether I'm the assignee or not).
## Custom queries
We use Redmine custom queries to have easy access to named searches
and visualizations that we either often need, or that we want to share
within a team or the overall Tails project.
In GitLab, the closest equivalent to Redmine custom queries is the URL
of an issues list or issues board filtered by assignee, milestone,
We will need ways to share these URLs and ideally, to name them.
We can do that on the GitLab project's description (home page),
on parent tracking tickets, on blueprints, on a team's
page on our website, and possibly in GitLab's own
[wiki](https://docs.gitlab.com/ce/user/project/wiki/) if we decide to
use it (either only for this use case or [[!tails_ticket 9174
desc="for our blueprints"]]).
# Merge requests
The [Protected branch
is probably the most adequate for regular contributors, since it's the
least disruptive in terms of workflow and habits and requires less
work to adjust our Jenkins CI setup:
- We mark our main branches (`stable`, `testing`, `devel`,
`feature/buster`) as "Protected". We give "Maintainer" access to
people allowed to push to these branches, i.e. what we previously
called "commit access".
- The Git workflow remains unchanged for regular developers who are
not granted full commit access: they get "Developer" access, can
push a topic branch to the canonical Git repository and our CI will
pick it up. The only difference is that they are not restricted to
pushing to their own `$nickname/*` namespace, which makes things
simpler and has other advantages, e.g. they can use the `wip/`
prefix (so our Jenkins CI ignores the branch) and collaborate with
others on shared branches.
- Other contributors get access strictly lower than "Developer".
They push topic branches to their own fork of the repository and
create merge requests.
- Our current Jenkins CI jobs generation process remains unchanged.
(Technically, we could adjust it so it generates CI jobs for _any_
merge request (based on `refs/merge-requests/*/head`), but this
would give arbitrary code execution on our CI to _anyone_.
Our infrastructure is not currently equipped to cope with this.)
It's out of scope for the first iteration but at some point, we might
want to migrate our blueprints to GitLab's
# Importing data from Redmine
### To be triaged
Relevant features — to be triaged as MUST/SHOULD/MAY:
* Preserve issue IDs so `#nnnn` should retain its meaning and links
like [[!tails_ticket 15878]] can be easily redirected.
* Preserve issue private/public status.
* Convert Textile to Markdown.
* Preserve watchers.
* Preserve issues relationships.
* Preserve other issues metadata.
* Does not require global GitLab administration token.
* Does not require SSH access to the GitLab machine.
A number of tools are available online.
These data migration tools come in various shape and some don't
support GitLab API v4, but generally there's a fork on GitHub that
fixes the most critical problems. Start there, explore the network
of forks, and follow the white GitHub rabbit(-hole):