Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dockerfile and ActiveCollab cloud support #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This dockerfile replicates the "how to" steps in this guide:
# https://github.com/JPustkuchen/active-collab-to-jira-migrator#how

FROM php:7.3.28-buster

# ---------- Install requirements ---------- #
RUN apt-get update && apt-get install -y wget git && \
# cleanup to reduce image size
apt-get clean && rm -rf /var/lib/apt/lists/*

# Install composer
RUN wget -O composer-setup.php https://getcomposer.org/installer && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
composer self-update


# ---------- Set up non-root user ---------- #
ENV USER=migrator
RUN useradd --create-home ${USER} -s /bin/bash && echo "alias ll='ls -alF'" >> /home/${USER}/.bashrc


# ---------- Set up application ---------- #
ARG SECRET=mysecret
ARG ACTIVE_COLLAB_URL=https://app.activecollab.com/123456/
ARG MEMORY_LIMIT=4096M

# copy repo files in to docker
COPY . /home/${USER}/active-collab-to-jira-migrator
RUN chown -R ${USER}:${USER} /home/${USER}

# switch to non-root user
WORKDIR /home/${USER}/active-collab-to-jira-migrator
USER ${USER}

# set up config
RUN mv config/EXAMPLE.config.php config/config.php && \
mv config/EXAMPLE.modifications.php config/modifications.php && \
sed -i "s#\$acUrl = 'https://ac.mycompany.com/';#\$acUrl = '${ACTIVE_COLLAB_URL}';#g" config/config.php && \
sed -i "s#// \$secret = urlencode('');#\$secret = urlencode('${SECRET}');#g" config/config.php && \
sed -i "s#ini_set('memory_limit', '4GB');#ini_set('memory_limit', '${MEMORY_LIMIT}');#g" config/config.php && \
composer install


ENTRYPOINT ["php", "-S", "0.0.0.0:8080", "-t", "app/"]
260 changes: 142 additions & 118 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,118 +1,142 @@
![PHP Composer](https://github.com/JPustkuchen/active-collab-to-jira-migrator/workflows/PHP%20Composer/badge.svg?branch=master)

# ActiveCollab to Jira® Migrator

*THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*

## Important notes
If you're happy with this project, please help to improve it by solving issues,
writing pull-requests and other improvements.
Always keep in mind, what you get for free here and give back.

We do NOT suggest anyone to move away from the wonderful [ActiveCollab](https://activecollab.com/) which we loved for nealy a decade
and still love. Anyway structures changed for us and ActiveCollab wasn't the right tool for us anymore.
But it might be different for you and [ActiveCollab](https://activecollab.com/) is still a very very good choice & wonderful software with a wonderful team.
THANK YOU VERY MUCH @ActiveCollab Team for all your great work!

If you need to move from ActiveCollab to Jira®, this tool may help you to do that and keep most of your assets.

## Dependencies?
- PHP >=7.3
- Composer
- Jira® Server >= 8.x (tested with 8.10.0)
- ActiveCollab >= 5. (tested with ActiveCollab 6.2.135)

## What?
ActiveCollabToJiraMigrator provides an export tool with UI, based on PHP and the [ActiveCollab Feather SDK](https://github.com/activecollab/activecollab-feather-sdk) using the [ActiveCollab SDK](https://developers.activecollab.com/api-documentation/) to create imports for Jira® based on the (JSON import functionality)[https://confluence.atlassian.com/adminjiraserver089/importing-data-from-json-1005346888.html].

### Exported / importable entities:
Most of these are only partially supported, because the systems structure is different:
- User => Users
- Project => Project
- Task => Task
- Subtask => Sub-task
- Comment => Comment
- Task attachment => Task attachmant
- Tracked time => Worklog

#### Not supported for example:
- Companies
- Task Lists / Milestones (migrated as label)
- Comment files / attachments
- Reactions
- Notes
- Discussions
- Receipts
- ...

## How?
0. Read this project documentation and code to understand what it does and how
it works.
1. Copy to your webserver and set `/app` as DocumentRoot for your VirtualHost.
like `thisactivecollabtojiramigrator.example.com` (Example)
2. Run `composer install` on the base directory via bash.
3. Copy `config/EXAMPLE.config.php` to `config/config.php`
4. Set all values in `config/config.php`, especially the _secret_
*Keep your secret safe and secure!*
5. Open https://thisactivecollabtojiramigrator.example.com/index.php?secret=_secret_
(Your secret from config.php) in your browser. You should see a form.
6. Enter your ActiveCollab Admin Credentials, set your offsets and limits (to allow splitting the export)
7. Export and check the results carefully

## Tips & Tricks
- Enable debug mode in config.php to see details and check for errors

## Important notes & limitations:
!! Due to limitations in Jira Import some ActiveCollab entities are not migrated at all. See below. !!
If you wish to add these functionalities, you may help to develop Export/JiraRestExporter which uses the Jira® API instead of the import.
Anyway some structural differences will never be 1:1 migratable like expenses, project expenses, non-task time records and others.
Here you'll need other workarounds or JiraApps which allow API-based creation.

!! Futhermore there are properties / settings which are not supported by Jira® Import which may also lead to unwanted access elevation. See below !!

## !! Important !!
1. The Jira® importer doesn't provide a sandbox environment. Data may be inconsistent if import fails.
So **TAKE BACKUPS** to be able to restore the clean state before the import!
2. Jira® has an issue with "External issue ID", which occurs if you split your import, see https://jira.atlassian.com/browse/JRASERVER-64477 - so you should try the workaround mentioned in this comment: https://jira.atlassian.com/browse/JRASERVER-64477?focusedCommentId=2274882&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-2274882 by Joe Harmon:
"There is another workaround that I found. The external issue ID is created per project that you import. The first time that I import it, I changed it to a global fields for any project. After that, after I import it doesn't duplicate the field anymore." and set the custom field global after the first import.
Due to this issue it might also be a good idea to create only one large import file.

### No processing implemented for:
- Project expenses
- Project time records
- Project task lists
- Project notes
- Project files
- Task expenses
- Comment attachments
These entities are not migrated at all.

Furthermore the following properties / settings are not supported:
- Project roles
- Attachments hidden from client

# References:

## ActiveCollab API and SDK:
- https://developers.activecollab.com/api-documentation/
- https://github.com/activecollab/activecollab-feather-sdk

## Jira JSON Import:
- https://confluence.atlassian.com/adminjiraserver089/importing-data-from-json-1005346888.html

## Additional resources
- The Jira JSON import documentation doesn't seem to list all available fields. So if you need further fields, you should follow this documentation (https://confluence.atlassian.com/jirakb/how-to-enable-json-export-in-jira-server-723158427.html) to
enable JSON export in tasks and export some tasks to see the exported format, which seems to match the import format.

# Copyrights / Trademarks
All product names, logos, and brands are property of their respective owners.
All company, product and service names used are for identification purposes only.
Use of these names, logos, and brands does not imply endorsement.
- [ActiveCollab](https://activecollab.com/)
- [Atlassian®, Jira® and Confluence® are registered trademarks of Atlassian](https://www.atlassian.com/legal/trademark)
![PHP Composer](https://github.com/JPustkuchen/active-collab-to-jira-migrator/workflows/PHP%20Composer/badge.svg?branch=master)

# ActiveCollab to Jira® Migrator

*THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*

## Important notes
If you're happy with this project, please help to improve it by solving issues,
writing pull-requests and other improvements.
Always keep in mind, what you get for free here and give back.

We do NOT suggest anyone to move away from the wonderful [ActiveCollab](https://activecollab.com/) which we loved for nealy a decade
and still love. Anyway structures changed for us and ActiveCollab wasn't the right tool for us anymore.
But it might be different for you and [ActiveCollab](https://activecollab.com/) is still a very very good choice & wonderful software with a wonderful team.
THANK YOU VERY MUCH @ActiveCollab Team for all your great work!

If you need to move from ActiveCollab to Jira®, this tool may help you to do that and keep most of your assets.

## Dependencies?
- PHP >=7.3
- Composer
- Jira® Server >= 8.x (tested with 8.10.0)
- ActiveCollab >= 5. (tested with ActiveCollab 6.2.135 and cloud)

## What?
ActiveCollabToJiraMigrator provides an export tool with UI, based on PHP and the [ActiveCollab Feather SDK](https://github.com/activecollab/activecollab-feather-sdk) using the [ActiveCollab SDK](https://developers.activecollab.com/api-documentation/) to create imports for Jira® based on the (JSON import functionality)[https://confluence.atlassian.com/adminjiraserver089/importing-data-from-json-1005346888.html].

### Exported / importable entities:
Most of these are only partially supported, because the systems structure is different:
- User => Users
- Project => Project
- Task => Task
- Subtask => Sub-task
- Comment => Comment
- Task attachment => Task attachmant
- Tracked time => Worklog

#### Not supported for example:
- Companies
- Task Lists / Milestones (migrated as label)
- Comment files / attachments
- Reactions
- Notes
- Discussions
- Receipts
- ...

## How?
0. Read this project documentation and code to understand what it does and how
it works.
1. Copy to your webserver and set `/app` as DocumentRoot for your VirtualHost.
like `thisactivecollabtojiramigrator.example.com` (Example)
2. Run `composer install` on the base directory via bash.
3. Copy `config/EXAMPLE.config.php` to `config/config.php`
4. Set all values in `config/config.php`, especially the _secret_
*Keep your secret safe and secure!*
5. Open https://thisactivecollabtojiramigrator.example.com/index.php?secret=_secret_
(Your secret from config.php) in your browser. You should see a form.
6. Enter your ActiveCollab Admin Credentials, set your offsets and limits (to allow splitting the export)
7. Export and check the results carefully

###*Alternative (Docker):*
*Tested locally running linux. Docker is required, see https://docs.docker.com/get-docker/*

Read the *how*-steps above and then take a look at the provided `Dockerfile` to understand if this option suits you.
This option replaces step 1-5 above.

Build docker image:
```bash
docker build . --rm --tag=active-collab-to-jira-migrator:latest \
--build-arg SECRET=mysuperduperstrongsecret \
--build-arg ACTIVE_COLLAB_URL=https://app.activecollab.com/123456/ \
--build-arg MEMORY_LIMIT=8192M
```
Start php service:
```bash
docker run -it --rm -p 8080:8080 --name=migrateservice active-collab-to-jira-migrator:latest
```

* To open a bash shell inside the docker: `docker exec -it migrateservice /bin/bash`
* to stop the running php server: `docker kill migrateservice`

Open your browser and go to http://localhost:8080/?secret=mysuperduperstrongsecret


## Tips & Tricks
- Enable debug mode in config.php to see details and check for errors

## Important notes & limitations:
!! Due to limitations in Jira Import some ActiveCollab entities are not migrated at all. See below. !!
If you wish to add these functionalities, you may help to develop Export/JiraRestExporter which uses the Jira® API instead of the import.
Anyway some structural differences will never be 1:1 migratable like expenses, project expenses, non-task time records and others.
Here you'll need other workarounds or JiraApps which allow API-based creation.

!! Futhermore there are properties / settings which are not supported by Jira® Import which may also lead to unwanted access elevation. See below !!

## !! Important !!
1. The Jira® importer doesn't provide a sandbox environment. Data may be inconsistent if import fails.
So **TAKE BACKUPS** to be able to restore the clean state before the import!
2. Jira® has an issue with "External issue ID", which occurs if you split your import, see https://jira.atlassian.com/browse/JRASERVER-64477 - so you should try the workaround mentioned in this comment: https://jira.atlassian.com/browse/JRASERVER-64477?focusedCommentId=2274882&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-2274882 by Joe Harmon:
"There is another workaround that I found. The external issue ID is created per project that you import. The first time that I import it, I changed it to a global fields for any project. After that, after I import it doesn't duplicate the field anymore." and set the custom field global after the first import.
Due to this issue it might also be a good idea to create only one large import file.

### No processing implemented for:
- Project expenses
- Project time records
- Project task lists
- Project notes
- Project files
- Task expenses
- Comment attachments
These entities are not migrated at all.

Furthermore the following properties / settings are not supported:
- Project roles
- Attachments hidden from client

# References:

## ActiveCollab API and SDK:
- https://developers.activecollab.com/api-documentation/
- https://github.com/activecollab/activecollab-feather-sdk

## Jira JSON Import:
- https://confluence.atlassian.com/adminjiraserver089/importing-data-from-json-1005346888.html

## Additional resources
- The Jira JSON import documentation doesn't seem to list all available fields. So if you need further fields, you should follow this documentation (https://confluence.atlassian.com/jirakb/how-to-enable-json-export-in-jira-server-723158427.html) to
enable JSON export in tasks and export some tasks to see the exported format, which seems to match the import format.

# Copyrights / Trademarks
All product names, logos, and brands are property of their respective owners.
All company, product and service names used are for identification purposes only.
Use of these names, logos, and brands does not imply endorsement.
- [ActiveCollab](https://activecollab.com/)
- [Atlassian®, Jira® and Confluence® are registered trademarks of Atlassian](https://www.atlassian.com/legal/trademark)
7 changes: 7 additions & 0 deletions app/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ <h1 class="text-center">ActiveCollab to Jira Migration</h1>
<fieldset>
<p>
<legend>Advanced options</legend>
<fieldset>
<legend>Self hosted or cloud version of ActiveCollab?</legend>
<input name="acType" type="radio" id=cloud value="cloud" checked>
<label for="cloud">Cloud</label><br />
<input name="acType" type="radio" id=selfhosted value="selfhosted">
<label for="selfhosted">Self hosted</label><br />
</fieldset>
<fieldset>
<legend>Project import</legend>
<label for="projectLimit">Project records limit:</label><br />
Expand Down
39 changes: 29 additions & 10 deletions app/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use ActiveCollab\SDK\Client;
use ActiveCollab\SDK\TokenInterface;
use ActiveCollab\SDK\Authenticator\Cloud;
use ActiveCollab\SDK\Authenticator\SelfHosted;
use ActiveCollab\SDK\Exceptions\Authentication;
use ActiveCollabToJiraMigrator\Export\JiraJsonExporter;
Expand Down Expand Up @@ -36,16 +37,34 @@
}

try {
// Construct a self-hosted authenticator. Last parameter is URL
// where your Active Collab.
$authenticator = new SelfHosted('ActiveCollab to Jira Migration Export',
'ActiveCollab to Jira Migration Export',
$_username,
$_password,
$acUrl);

// Issue a token.
$token = $authenticator->issueToken();
$_acTypeRadio = filter_input(INPUT_POST, 'acType', FILTER_SANITIZE_STRING);
if($_acTypeRadio == 'cloud'){
// Construct a cloud authenticator.
$authenticator = new Cloud('ActiveCollab to Jira Migration Export',
'ActiveCollab to Jira Migration Export',
$_username,
$_password);

// Get user ID
$accounts = $authenticator->getAccounts();
// TODO: support multiple ActiveCollab accounts, reset will get first element in array of accounts
$account = reset($accounts)['id'];

// Issue a token.
$token = $authenticator->issueToken($account);
} elseif($_acTypeRadio == 'selfhosted'){
// Construct a self-hosted authenticator. Last parameter is URL where your Active Collab.
$authenticator = new SelfHosted('ActiveCollab to Jira Migration Export',
'ActiveCollab to Jira Migration Export',
$_username,
$_password,
$acUrl);

// Issue a token.
$token = $authenticator->issueToken();
} else {
throw new \Exception('Unknown ActiveCollab type, you should select cloud or self hosted');
}
if ($token instanceof TokenInterface) {
// Login successful!
$acUrl = $token->getUrl();
Expand Down